1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.telephony; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.SuppressLint; 24 import android.annotation.SystemApi; 25 import android.app.Service; 26 import android.content.Intent; 27 import android.net.Uri; 28 import android.os.Build; 29 import android.os.CancellationSignal; 30 import android.os.IBinder; 31 import android.os.Parcel; 32 import android.os.Parcelable; 33 import android.os.RemoteException; 34 import android.telephony.Annotation.DisconnectCauses; 35 import android.telephony.Annotation.PreciseDisconnectCauses; 36 import android.telephony.ims.ImsReasonInfo; 37 import android.text.TextUtils; 38 import android.util.Log; 39 40 import com.android.internal.telephony.IDomainSelectionServiceController; 41 import com.android.internal.telephony.IDomainSelector; 42 import com.android.internal.telephony.ITransportSelectorCallback; 43 import com.android.internal.telephony.ITransportSelectorResultCallback; 44 import com.android.internal.telephony.IWwanSelectorCallback; 45 import com.android.internal.telephony.IWwanSelectorResultCallback; 46 import com.android.internal.telephony.flags.Flags; 47 import com.android.internal.telephony.util.TelephonyUtils; 48 import com.android.telephony.Rlog; 49 50 import java.lang.annotation.Retention; 51 import java.lang.annotation.RetentionPolicy; 52 import java.lang.ref.WeakReference; 53 import java.util.List; 54 import java.util.Objects; 55 import java.util.concurrent.CancellationException; 56 import java.util.concurrent.CompletableFuture; 57 import java.util.concurrent.CompletionException; 58 import java.util.concurrent.Executor; 59 import java.util.function.Consumer; 60 61 /** 62 * Base domain selection implementation. 63 * <p> 64 * Services that extend {@link DomainSelectionService} must register the service in their 65 * AndroidManifest.xml to be detected by the framework. 66 * <p> 67 * 1) The application must declare that they use the 68 * android.permission.BIND_DOMAIN_SELECTION_SERVICE permission. 69 * <p> 70 * 2) The DomainSelectionService definition in the manifest must follow this format: 71 * <pre> 72 * {@code 73 * ... 74 * <service android:name=".EgDomainSelectionService" 75 * android:permission="android.permission.BIND_DOMAIN_SELECTION_SERVICE" > 76 * <intent-filter> 77 * <action android:name="android.telephony.DomainSelectionService" /> 78 * </intent-filter> 79 * </service> 80 * ... 81 * } 82 * </pre> 83 * <p> 84 * The ComponentName corresponding to this DomainSelectionService component MUST also be set 85 * as the system domain selection implementation in order to be bound. 86 * The system domain selection implementation is set in the device overlay for 87 * {@code config_domain_selection_service_component_name} 88 * in {@code packages/services/Telephony/res/values/config.xml}. 89 * 90 * @hide 91 */ 92 @SystemApi 93 @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) 94 public abstract class DomainSelectionService extends Service { 95 96 private static final String LOG_TAG = "DomainSelectionService"; 97 98 /** 99 * The intent that must be defined as an intent-filter in the AndroidManifest of the 100 * {@link DomainSelectionService}. 101 * 102 * @hide 103 */ 104 public static final String SERVICE_INTERFACE = "android.telephony.DomainSelectionService"; 105 106 /** @hide */ 107 @Retention(RetentionPolicy.SOURCE) 108 @IntDef(prefix = "SELECTOR_TYPE_", 109 value = { 110 SELECTOR_TYPE_CALLING, 111 SELECTOR_TYPE_SMS}) 112 public @interface SelectorType {} 113 114 /** Indicates the domain selector type for calling. */ 115 public static final int SELECTOR_TYPE_CALLING = 1; 116 /** Indicates the domain selector type for sms. */ 117 public static final int SELECTOR_TYPE_SMS = 2; 118 119 /** Indicates that the modem can scan for emergency service as per modem’s implementation. */ 120 public static final int SCAN_TYPE_NO_PREFERENCE = 0; 121 122 /** Indicates that the modem will scan for emergency service in limited service mode. */ 123 public static final int SCAN_TYPE_LIMITED_SERVICE = 1; 124 125 /** Indicates that the modem will scan for emergency service in full service mode. */ 126 public static final int SCAN_TYPE_FULL_SERVICE = 2; 127 128 /** @hide */ 129 @Retention(RetentionPolicy.SOURCE) 130 @IntDef(prefix = "SCAN_TYPE_", 131 value = { 132 SCAN_TYPE_NO_PREFERENCE, 133 SCAN_TYPE_LIMITED_SERVICE, 134 SCAN_TYPE_FULL_SERVICE}) 135 public @interface EmergencyScanType {} 136 137 /** 138 * Contains attributes required to determine the domain for a telephony service. 139 */ 140 @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) 141 public static final class SelectionAttributes implements Parcelable { 142 143 private static final String TAG = "SelectionAttributes"; 144 145 private int mSlotIndex; 146 private int mSubId; 147 private @Nullable String mCallId; 148 private @Nullable Uri mAddress; 149 private @SelectorType int mSelectorType; 150 private boolean mIsVideoCall; 151 private boolean mIsEmergency; 152 private boolean mIsTestEmergencyNumber; 153 private boolean mIsExitedFromAirplaneMode; 154 private @Nullable ImsReasonInfo mImsReasonInfo; 155 private @PreciseDisconnectCauses int mCause; 156 private @Nullable EmergencyRegistrationResult mEmergencyRegistrationResult; 157 158 /** 159 * @param slotIndex The logical slot index. 160 * @param subscriptionId The subscription identifier. 161 * @param callId The call identifier. 162 * @param address The dialed address. 163 * @param selectorType Indicates the requested domain selector type. 164 * @param video Indicates it's a video call. 165 * @param emergency Indicates it's emergency service. 166 * @param isTest Indicates it's a test emergency number. 167 * @param exited {@code true} if the request caused the device to move out of airplane mode. 168 * @param imsReasonInfo The reason why the last PS attempt failed. 169 * @param cause The reason why the last CS attempt failed. 170 * @param regResult The current registration result for emergency services. 171 */ SelectionAttributes(int slotIndex, int subscriptionId, @Nullable String callId, @Nullable Uri address, @SelectorType int selectorType, boolean video, boolean emergency, boolean isTest, boolean exited, @Nullable ImsReasonInfo imsReasonInfo, @PreciseDisconnectCauses int cause, @Nullable EmergencyRegistrationResult regResult)172 private SelectionAttributes(int slotIndex, int subscriptionId, @Nullable String callId, 173 @Nullable Uri address, @SelectorType int selectorType, 174 boolean video, boolean emergency, boolean isTest, boolean exited, 175 @Nullable ImsReasonInfo imsReasonInfo, @PreciseDisconnectCauses int cause, 176 @Nullable EmergencyRegistrationResult regResult) { 177 mSlotIndex = slotIndex; 178 mSubId = subscriptionId; 179 mCallId = callId; 180 mAddress = address; 181 mSelectorType = selectorType; 182 mIsVideoCall = video; 183 mIsEmergency = emergency; 184 mIsTestEmergencyNumber = isTest; 185 mIsExitedFromAirplaneMode = exited; 186 mImsReasonInfo = imsReasonInfo; 187 mCause = cause; 188 mEmergencyRegistrationResult = regResult; 189 } 190 191 /** 192 * Copy constructor. 193 * 194 * @param s Source selection attributes. 195 * @hide 196 */ SelectionAttributes(@onNull SelectionAttributes s)197 public SelectionAttributes(@NonNull SelectionAttributes s) { 198 mSlotIndex = s.mSlotIndex; 199 mSubId = s.mSubId; 200 mCallId = s.mCallId; 201 mAddress = s.mAddress; 202 mSelectorType = s.mSelectorType; 203 mIsEmergency = s.mIsEmergency; 204 mIsTestEmergencyNumber = s.mIsTestEmergencyNumber; 205 mIsExitedFromAirplaneMode = s.mIsExitedFromAirplaneMode; 206 mImsReasonInfo = s.mImsReasonInfo; 207 mCause = s.mCause; 208 mEmergencyRegistrationResult = s.mEmergencyRegistrationResult; 209 } 210 211 /** 212 * Constructs a SelectionAttributes object from the given parcel. 213 */ SelectionAttributes(@onNull Parcel in)214 private SelectionAttributes(@NonNull Parcel in) { 215 readFromParcel(in); 216 } 217 218 /** 219 * @return The logical slot index. 220 */ getSlotIndex()221 public int getSlotIndex() { 222 return mSlotIndex; 223 } 224 225 /** 226 * @return The subscription identifier. 227 */ getSubscriptionId()228 public int getSubscriptionId() { 229 return mSubId; 230 } 231 232 /** 233 * @return The call identifier. 234 */ getCallId()235 public @Nullable String getCallId() { 236 return mCallId; 237 } 238 239 /** 240 * @return The dialed address. 241 */ getAddress()242 public @Nullable Uri getAddress() { 243 return mAddress; 244 } 245 246 /** 247 * @return The domain selector type. 248 */ getSelectorType()249 public @SelectorType int getSelectorType() { 250 return mSelectorType; 251 } 252 253 /** 254 * @return {@code true} if the request is for a video call. 255 */ isVideoCall()256 public boolean isVideoCall() { 257 return mIsVideoCall; 258 } 259 260 /** 261 * @return {@code true} if the request is for emergency services. 262 */ isEmergency()263 public boolean isEmergency() { 264 return mIsEmergency; 265 } 266 267 /** 268 * @return {@code true} if the dialed number is a test emergency number. 269 */ isTestEmergencyNumber()270 public boolean isTestEmergencyNumber() { 271 return mIsTestEmergencyNumber; 272 } 273 274 /** 275 * @return {@code true} if the request caused the device to move out of airplane mode. 276 */ isExitedFromAirplaneMode()277 public boolean isExitedFromAirplaneMode() { 278 return mIsExitedFromAirplaneMode; 279 } 280 281 /** 282 * @return The PS disconnect cause if trying over PS resulted in a failure and 283 * reselection is required. 284 */ getPsDisconnectCause()285 public @Nullable ImsReasonInfo getPsDisconnectCause() { 286 return mImsReasonInfo; 287 } 288 289 /** 290 * @return The CS disconnect cause if trying over CS resulted in a failure and 291 * reselection is required. 292 */ getCsDisconnectCause()293 public @PreciseDisconnectCauses int getCsDisconnectCause() { 294 return mCause; 295 } 296 297 /** 298 * @return The current registration state of cellular network. 299 */ getEmergencyRegistrationResult()300 public @Nullable EmergencyRegistrationResult getEmergencyRegistrationResult() { 301 return mEmergencyRegistrationResult; 302 } 303 304 @Override toString()305 public @NonNull String toString() { 306 return "{ slotIndex=" + mSlotIndex 307 + ", subId=" + mSubId 308 + ", callId=" + mCallId 309 + ", address=" + (Build.IS_DEBUGGABLE ? mAddress : "***") 310 + ", type=" + mSelectorType 311 + ", videoCall=" + mIsVideoCall 312 + ", emergency=" + mIsEmergency 313 + ", isTest=" + mIsTestEmergencyNumber 314 + ", airplaneMode=" + mIsExitedFromAirplaneMode 315 + ", reasonInfo=" + mImsReasonInfo 316 + ", cause=" + mCause 317 + ", regResult=" + mEmergencyRegistrationResult 318 + " }"; 319 } 320 321 @Override equals(Object o)322 public boolean equals(Object o) { 323 if (this == o) return true; 324 if (o == null || getClass() != o.getClass()) return false; 325 SelectionAttributes that = (SelectionAttributes) o; 326 return mSlotIndex == that.mSlotIndex && mSubId == that.mSubId 327 && TextUtils.equals(mCallId, that.mCallId) 328 && equalsHandlesNulls(mAddress, that.mAddress) 329 && mSelectorType == that.mSelectorType && mIsVideoCall == that.mIsVideoCall 330 && mIsEmergency == that.mIsEmergency 331 && mIsTestEmergencyNumber == that.mIsTestEmergencyNumber 332 && mIsExitedFromAirplaneMode == that.mIsExitedFromAirplaneMode 333 && equalsHandlesNulls(mImsReasonInfo, that.mImsReasonInfo) 334 && mCause == that.mCause 335 && equalsHandlesNulls(mEmergencyRegistrationResult, 336 that.mEmergencyRegistrationResult); 337 } 338 339 @Override hashCode()340 public int hashCode() { 341 return Objects.hash(mCallId, mAddress, mImsReasonInfo, 342 mIsVideoCall, mIsEmergency, mIsTestEmergencyNumber, mIsExitedFromAirplaneMode, 343 mEmergencyRegistrationResult, mSlotIndex, mSubId, mSelectorType, mCause); 344 } 345 346 @Override describeContents()347 public int describeContents() { 348 return 0; 349 } 350 351 @Override writeToParcel(@onNull Parcel out, int flags)352 public void writeToParcel(@NonNull Parcel out, int flags) { 353 out.writeInt(mSlotIndex); 354 out.writeInt(mSubId); 355 out.writeString8(mCallId); 356 out.writeParcelable(mAddress, 0); 357 out.writeInt(mSelectorType); 358 out.writeBoolean(mIsVideoCall); 359 out.writeBoolean(mIsEmergency); 360 out.writeBoolean(mIsTestEmergencyNumber); 361 out.writeBoolean(mIsExitedFromAirplaneMode); 362 out.writeParcelable(mImsReasonInfo, 0); 363 out.writeInt(mCause); 364 out.writeParcelable(mEmergencyRegistrationResult, 0); 365 } 366 readFromParcel(@onNull Parcel in)367 private void readFromParcel(@NonNull Parcel in) { 368 mSlotIndex = in.readInt(); 369 mSubId = in.readInt(); 370 mCallId = in.readString8(); 371 mAddress = in.readParcelable(Uri.class.getClassLoader(), 372 android.net.Uri.class); 373 mSelectorType = in.readInt(); 374 mIsVideoCall = in.readBoolean(); 375 mIsEmergency = in.readBoolean(); 376 mIsTestEmergencyNumber = in.readBoolean(); 377 mIsExitedFromAirplaneMode = in.readBoolean(); 378 mImsReasonInfo = in.readParcelable(ImsReasonInfo.class.getClassLoader(), 379 android.telephony.ims.ImsReasonInfo.class); 380 mCause = in.readInt(); 381 mEmergencyRegistrationResult = in.readParcelable( 382 EmergencyRegistrationResult.class.getClassLoader(), 383 EmergencyRegistrationResult.class); 384 } 385 386 public static final @NonNull Creator<SelectionAttributes> CREATOR = 387 new Creator<SelectionAttributes>() { 388 @Override 389 public SelectionAttributes createFromParcel(@NonNull Parcel in) { 390 return new SelectionAttributes(in); 391 } 392 393 @Override 394 public SelectionAttributes[] newArray(int size) { 395 return new SelectionAttributes[size]; 396 } 397 }; 398 equalsHandlesNulls(Object a, Object b)399 private static boolean equalsHandlesNulls(Object a, Object b) { 400 return (a == null) ? (b == null) : a.equals(b); 401 } 402 403 /** 404 * Builder class creating a new instance. 405 */ 406 @FlaggedApi(Flags.FLAG_USE_OEM_DOMAIN_SELECTION_SERVICE) 407 public static final class Builder { 408 private final int mSlotIndex; 409 private final int mSubId; 410 private @Nullable String mCallId; 411 private @Nullable Uri mAddress; 412 private final @SelectorType int mSelectorType; 413 private boolean mIsVideoCall; 414 private boolean mIsEmergency; 415 private boolean mIsTestEmergencyNumber; 416 private boolean mIsExitedFromAirplaneMode; 417 private @Nullable ImsReasonInfo mImsReasonInfo; 418 private @PreciseDisconnectCauses int mCause; 419 private @Nullable EmergencyRegistrationResult mEmergencyRegistrationResult; 420 421 /** 422 * Default constructor for Builder. 423 */ Builder(int slotIndex, int subscriptionId, @SelectorType int selectorType)424 public Builder(int slotIndex, int subscriptionId, @SelectorType int selectorType) { 425 mSlotIndex = slotIndex; 426 mSubId = subscriptionId; 427 mSelectorType = selectorType; 428 } 429 430 /** 431 * Sets the call identifier. 432 * 433 * @param callId The call identifier. 434 * @return The same instance of the builder. 435 */ setCallId(@ullable String callId)436 public @NonNull Builder setCallId(@Nullable String callId) { 437 mCallId = callId; 438 return this; 439 } 440 441 /** 442 * Sets the dialed address. 443 * 444 * @param address The dialed address. 445 * @return The same instance of the builder. 446 */ setAddress(@ullable Uri address)447 public @NonNull Builder setAddress(@Nullable Uri address) { 448 mAddress = address; 449 return this; 450 } 451 452 /** 453 * Sets whether it's a video call or not. 454 * 455 * @param isVideo Indicates it's a video call. 456 * @return The same instance of the builder. 457 */ setVideoCall(boolean isVideo)458 public @NonNull Builder setVideoCall(boolean isVideo) { 459 mIsVideoCall = isVideo; 460 return this; 461 } 462 463 /** 464 * Sets whether it's an emergency service or not. 465 * 466 * @param isEmergency Indicates it's emergency service. 467 * @return The same instance of the builder. 468 */ setEmergency(boolean isEmergency)469 public @NonNull Builder setEmergency(boolean isEmergency) { 470 mIsEmergency = isEmergency; 471 return this; 472 } 473 474 /** 475 * Sets whether it's a test emergency number or not. 476 * 477 * @param isTest Indicates it's a test emergency number. 478 * @return The same instance of the builder. 479 */ setTestEmergencyNumber(boolean isTest)480 public @NonNull Builder setTestEmergencyNumber(boolean isTest) { 481 mIsTestEmergencyNumber = isTest; 482 return this; 483 } 484 485 /** 486 * Sets whether the request caused the device to move out of airplane mode. 487 * 488 * @param exited {@code true} if the request caused the device to move out of 489 * airplane mode. 490 * @return The same instance of the builder. 491 */ setExitedFromAirplaneMode(boolean exited)492 public @NonNull Builder setExitedFromAirplaneMode(boolean exited) { 493 mIsExitedFromAirplaneMode = exited; 494 return this; 495 } 496 497 /** 498 * Sets an optional reason why the last PS attempt failed. 499 * 500 * @param info The reason why the last PS attempt failed. 501 * @return The same instance of the builder. 502 */ setPsDisconnectCause(@ullable ImsReasonInfo info)503 public @NonNull Builder setPsDisconnectCause(@Nullable ImsReasonInfo info) { 504 mImsReasonInfo = info; 505 return this; 506 } 507 508 /** 509 * Sets an optional reason why the last CS attempt failed. 510 * 511 * @param cause The reason why the last CS attempt failed. 512 * @return The same instance of the builder. 513 */ setCsDisconnectCause(@reciseDisconnectCauses int cause)514 public @NonNull Builder setCsDisconnectCause(@PreciseDisconnectCauses int cause) { 515 mCause = cause; 516 return this; 517 } 518 519 /** 520 * Sets the current registration result for emergency services. 521 * 522 * @param regResult The current registration result for emergency services. 523 * @return The same instance of the builder. 524 */ setEmergencyRegistrationResult( @ullable EmergencyRegistrationResult regResult)525 public @NonNull Builder setEmergencyRegistrationResult( 526 @Nullable EmergencyRegistrationResult regResult) { 527 mEmergencyRegistrationResult = regResult; 528 return this; 529 } 530 531 /** 532 * Build the SelectionAttributes. 533 * @return The SelectionAttributes object. 534 */ build()535 public @NonNull SelectionAttributes build() { 536 return new SelectionAttributes(mSlotIndex, mSubId, mCallId, mAddress, 537 mSelectorType, mIsVideoCall, mIsEmergency, mIsTestEmergencyNumber, 538 mIsExitedFromAirplaneMode, mImsReasonInfo, 539 mCause, mEmergencyRegistrationResult); 540 } 541 } 542 } 543 544 /** 545 * A wrapper class for ITransportSelectorCallback interface. 546 */ 547 private final class TransportSelectorCallbackWrapper implements TransportSelectorCallback { 548 private static final String TAG = "TransportSelectorCallbackWrapper"; 549 550 private final @NonNull ITransportSelectorCallback mCallback; 551 private final @NonNull Executor mExecutor; 552 553 private @Nullable ITransportSelectorResultCallbackAdapter mResultCallback; 554 private @Nullable DomainSelectorWrapper mSelectorWrapper; 555 TransportSelectorCallbackWrapper(@onNull ITransportSelectorCallback cb, @NonNull Executor executor)556 TransportSelectorCallbackWrapper(@NonNull ITransportSelectorCallback cb, 557 @NonNull Executor executor) { 558 mCallback = cb; 559 mExecutor = executor; 560 } 561 562 @Override onCreated(@onNull DomainSelector selector)563 public void onCreated(@NonNull DomainSelector selector) { 564 try { 565 mSelectorWrapper = new DomainSelectorWrapper(selector, mExecutor); 566 mCallback.onCreated(mSelectorWrapper.getCallbackBinder()); 567 } catch (Exception e) { 568 Rlog.e(TAG, "onCreated e=" + e); 569 } 570 } 571 572 @Override onWlanSelected(boolean useEmergencyPdn)573 public void onWlanSelected(boolean useEmergencyPdn) { 574 try { 575 mCallback.onWlanSelected(useEmergencyPdn); 576 } catch (Exception e) { 577 Rlog.e(TAG, "onWlanSelected e=" + e); 578 } 579 } 580 581 @Override onWwanSelected(Consumer<WwanSelectorCallback> consumer)582 public void onWwanSelected(Consumer<WwanSelectorCallback> consumer) { 583 try { 584 mResultCallback = new ITransportSelectorResultCallbackAdapter(consumer, mExecutor); 585 mCallback.onWwanSelectedAsync(mResultCallback); 586 } catch (Exception e) { 587 Rlog.e(TAG, "onWwanSelected e=" + e); 588 executeMethodAsyncNoException(mExecutor, 589 () -> consumer.accept(null), TAG, "onWwanSelectedAsync-Exception"); 590 } 591 } 592 593 @Override onSelectionTerminated(@isconnectCauses int cause)594 public void onSelectionTerminated(@DisconnectCauses int cause) { 595 try { 596 mCallback.onSelectionTerminated(cause); 597 mSelectorWrapper = null; 598 } catch (Exception e) { 599 Rlog.e(TAG, "onSelectionTerminated e=" + e); 600 } 601 } 602 603 private class ITransportSelectorResultCallbackAdapter 604 extends ITransportSelectorResultCallback.Stub { 605 private final @NonNull Consumer<WwanSelectorCallback> mConsumer; 606 private final @NonNull Executor mExecutor; 607 ITransportSelectorResultCallbackAdapter( @onNull Consumer<WwanSelectorCallback> consumer, @NonNull Executor executor)608 ITransportSelectorResultCallbackAdapter( 609 @NonNull Consumer<WwanSelectorCallback> consumer, 610 @NonNull Executor executor) { 611 mConsumer = consumer; 612 mExecutor = executor; 613 } 614 615 @Override onCompleted(@onNull IWwanSelectorCallback cb)616 public void onCompleted(@NonNull IWwanSelectorCallback cb) { 617 if (mConsumer == null) return; 618 619 WwanSelectorCallback callback = new WwanSelectorCallbackWrapper(cb, mExecutor); 620 executeMethodAsyncNoException(mExecutor, 621 () -> mConsumer.accept(callback), TAG, "onWwanSelectedAsync-Completed"); 622 } 623 } 624 } 625 626 /** 627 * A wrapper class for IDomainSelector interface. 628 */ 629 private final class DomainSelectorWrapper { 630 private static final String TAG = "DomainSelectorWrapper"; 631 632 private @NonNull IDomainSelector mCallbackBinder; 633 DomainSelectorWrapper(@onNull DomainSelector cb, @NonNull Executor executor)634 DomainSelectorWrapper(@NonNull DomainSelector cb, @NonNull Executor executor) { 635 mCallbackBinder = new IDomainSelectorAdapter(cb, executor); 636 } 637 638 private class IDomainSelectorAdapter extends IDomainSelector.Stub { 639 private final @NonNull WeakReference<DomainSelector> mDomainSelectorWeakRef; 640 private final @NonNull Executor mExecutor; 641 IDomainSelectorAdapter(@onNull DomainSelector domainSelector, @NonNull Executor executor)642 IDomainSelectorAdapter(@NonNull DomainSelector domainSelector, 643 @NonNull Executor executor) { 644 mDomainSelectorWeakRef = 645 new WeakReference<DomainSelector>(domainSelector); 646 mExecutor = executor; 647 } 648 649 @Override reselectDomain(@onNull SelectionAttributes attr)650 public void reselectDomain(@NonNull SelectionAttributes attr) { 651 final DomainSelector domainSelector = mDomainSelectorWeakRef.get(); 652 if (domainSelector == null) return; 653 654 executeMethodAsyncNoException(mExecutor, 655 () -> domainSelector.reselectDomain(attr), TAG, "reselectDomain"); 656 } 657 658 @Override finishSelection()659 public void finishSelection() { 660 final DomainSelector domainSelector = mDomainSelectorWeakRef.get(); 661 if (domainSelector == null) return; 662 663 executeMethodAsyncNoException(mExecutor, 664 () -> domainSelector.finishSelection(), TAG, "finishSelection"); 665 } 666 } 667 getCallbackBinder()668 public @NonNull IDomainSelector getCallbackBinder() { 669 return mCallbackBinder; 670 } 671 } 672 673 /** 674 * A wrapper class for IWwanSelectorCallback and IWwanSelectorResultCallback. 675 */ 676 private final class WwanSelectorCallbackWrapper 677 implements WwanSelectorCallback, CancellationSignal.OnCancelListener { 678 private static final String TAG = "WwanSelectorCallbackWrapper"; 679 680 private final @NonNull IWwanSelectorCallback mCallback; 681 private final @NonNull Executor mExecutor; 682 683 private @Nullable IWwanSelectorResultCallbackAdapter mResultCallback; 684 WwanSelectorCallbackWrapper(@onNull IWwanSelectorCallback cb, @NonNull Executor executor)685 WwanSelectorCallbackWrapper(@NonNull IWwanSelectorCallback cb, 686 @NonNull Executor executor) { 687 mCallback = cb; 688 mExecutor = executor; 689 } 690 691 @Override onCancel()692 public void onCancel() { 693 try { 694 mCallback.onCancel(); 695 } catch (Exception e) { 696 Rlog.e(TAG, "onCancel e=" + e); 697 } 698 } 699 700 @Override onRequestEmergencyNetworkScan(@onNull List<Integer> preferredNetworks, @EmergencyScanType int scanType, boolean resetScan, @NonNull CancellationSignal signal, @NonNull Consumer<EmergencyRegistrationResult> consumer)701 public void onRequestEmergencyNetworkScan(@NonNull List<Integer> preferredNetworks, 702 @EmergencyScanType int scanType, boolean resetScan, 703 @NonNull CancellationSignal signal, 704 @NonNull Consumer<EmergencyRegistrationResult> consumer) { 705 try { 706 if (signal != null) signal.setOnCancelListener(this); 707 mResultCallback = new IWwanSelectorResultCallbackAdapter(consumer, mExecutor); 708 mCallback.onRequestEmergencyNetworkScan( 709 preferredNetworks.stream().mapToInt(Integer::intValue).toArray(), 710 scanType, resetScan, mResultCallback); 711 } catch (Exception e) { 712 Rlog.e(TAG, "onRequestEmergencyNetworkScan e=" + e); 713 } 714 } 715 716 @Override onDomainSelected(@etworkRegistrationInfo.Domain int domain, boolean useEmergencyPdn)717 public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain, 718 boolean useEmergencyPdn) { 719 try { 720 mCallback.onDomainSelected(domain, useEmergencyPdn); 721 } catch (Exception e) { 722 Rlog.e(TAG, "onDomainSelected e=" + e); 723 } 724 } 725 726 private class IWwanSelectorResultCallbackAdapter 727 extends IWwanSelectorResultCallback.Stub { 728 private final @NonNull Consumer<EmergencyRegistrationResult> mConsumer; 729 private final @NonNull Executor mExecutor; 730 IWwanSelectorResultCallbackAdapter( @onNull Consumer<EmergencyRegistrationResult> consumer, @NonNull Executor executor)731 IWwanSelectorResultCallbackAdapter( 732 @NonNull Consumer<EmergencyRegistrationResult> consumer, 733 @NonNull Executor executor) { 734 mConsumer = consumer; 735 mExecutor = executor; 736 } 737 738 @Override onComplete(@onNull EmergencyRegistrationResult result)739 public void onComplete(@NonNull EmergencyRegistrationResult result) { 740 if (mConsumer == null) return; 741 742 executeMethodAsyncNoException(mExecutor, 743 () -> mConsumer.accept(result), TAG, "onScanComplete"); 744 } 745 } 746 } 747 748 private final Object mExecutorLock = new Object(); 749 750 /** Executor used to execute methods called remotely by the framework. */ 751 private @NonNull Executor mExecutor; 752 753 /** 754 * Selects a calling domain given the SelectionAttributes of the call request. 755 * <p> 756 * When the framework generates a request to place a call, {@link #onDomainSelection} 757 * will be called in order to determine the domain (CS or PS). For PS calls, the transport 758 * (WWAN or WLAN) will also need to be determined. 759 * <p> 760 * Once the domain/transport has been selected or an error has occurred, 761 * {@link TransportSelectorCallback} must be used to communicate the result back 762 * to the framework. 763 * 764 * @param attr Required to determine the domain. 765 * @param callback The callback instance being registered. 766 */ onDomainSelection(@onNull SelectionAttributes attr, @NonNull TransportSelectorCallback callback)767 public abstract void onDomainSelection(@NonNull SelectionAttributes attr, 768 @NonNull TransportSelectorCallback callback); 769 770 /** 771 * Notifies the change in {@link ServiceState} for a specific logical slot index. 772 * 773 * @param slotIndex For which the state changed. 774 * @param subscriptionId For which the state changed. 775 * @param serviceState Updated {@link ServiceState}. 776 */ onServiceStateUpdated(int slotIndex, int subscriptionId, @NonNull ServiceState serviceState)777 public void onServiceStateUpdated(int slotIndex, int subscriptionId, 778 @NonNull ServiceState serviceState) { 779 } 780 781 /** 782 * Notifies the change in {@link BarringInfo} for a specific logical slot index. 783 * 784 * @param slotIndex For which the state changed. 785 * @param subscriptionId For which the state changed. 786 * @param info Updated {@link BarringInfo}. 787 */ onBarringInfoUpdated(int slotIndex, int subscriptionId, @NonNull BarringInfo info)788 public void onBarringInfoUpdated(int slotIndex, int subscriptionId, @NonNull BarringInfo info) { 789 } 790 791 private final IBinder mDomainSelectionServiceController = 792 new IDomainSelectionServiceController.Stub() { 793 @Override 794 public void selectDomain(@NonNull SelectionAttributes attr, 795 @NonNull ITransportSelectorCallback callback) throws RemoteException { 796 executeMethodAsync(getCachedExecutor(), 797 () -> DomainSelectionService.this.onDomainSelection(attr, 798 new TransportSelectorCallbackWrapper(callback, getCachedExecutor())), 799 LOG_TAG, "onDomainSelection"); 800 } 801 802 @Override 803 public void updateServiceState(int slotIndex, int subscriptionId, 804 @NonNull ServiceState serviceState) { 805 executeMethodAsyncNoException(getCachedExecutor(), 806 () -> DomainSelectionService.this.onServiceStateUpdated(slotIndex, 807 subscriptionId, serviceState), LOG_TAG, "onServiceStateUpdated"); 808 } 809 810 @Override 811 public void updateBarringInfo(int slotIndex, int subscriptionId, 812 @NonNull BarringInfo info) { 813 executeMethodAsyncNoException(getCachedExecutor(), 814 () -> DomainSelectionService.this.onBarringInfoUpdated(slotIndex, 815 subscriptionId, info), 816 LOG_TAG, "onBarringInfoUpdated"); 817 } 818 }; 819 executeMethodAsync(@onNull Executor executor, @NonNull Runnable r, @NonNull String tag, @NonNull String errorLogName)820 private static void executeMethodAsync(@NonNull Executor executor, @NonNull Runnable r, 821 @NonNull String tag, @NonNull String errorLogName) throws RemoteException { 822 try { 823 CompletableFuture.runAsync( 824 () -> TelephonyUtils.runWithCleanCallingIdentity(r), executor).join(); 825 } catch (CancellationException | CompletionException e) { 826 Rlog.w(tag, "Binder - " + errorLogName + " exception: " + e.getMessage()); 827 throw new RemoteException(e.getMessage()); 828 } 829 } 830 executeMethodAsyncNoException(@onNull Executor executor, @NonNull Runnable r, @NonNull String tag, @NonNull String errorLogName)831 private void executeMethodAsyncNoException(@NonNull Executor executor, @NonNull Runnable r, 832 @NonNull String tag, @NonNull String errorLogName) { 833 try { 834 CompletableFuture.runAsync( 835 () -> TelephonyUtils.runWithCleanCallingIdentity(r), executor); 836 } catch (CancellationException | CompletionException e) { 837 Rlog.w(tag, "Binder - " + errorLogName + " exception: " + e.getMessage()); 838 } 839 } 840 841 /** @hide */ 842 @Override onBind(@ullable Intent intent)843 public final @Nullable IBinder onBind(@Nullable Intent intent) { 844 if (intent == null) return null; 845 if (SERVICE_INTERFACE.equals(intent.getAction())) { 846 Log.i(LOG_TAG, "DomainSelectionService Bound."); 847 return mDomainSelectionServiceController; 848 } 849 return null; 850 } 851 852 /** 853 * The Executor to use when calling callback methods from the framework. 854 * <p> 855 * By default, calls from the framework will use Binder threads to call these methods. 856 * 857 * @return an {@link Executor} used to execute methods called remotely by the framework. 858 */ 859 @SuppressLint("OnNameExpected") getCreateExecutor()860 public @NonNull Executor getCreateExecutor() { 861 return Runnable::run; 862 } 863 864 /** 865 * Gets the {@link Executor} which executes methods of this service. 866 * This method should be private when this service is implemented in a separated process 867 * other than telephony framework. 868 * @return {@link Executor} instance. 869 * @hide 870 */ getCachedExecutor()871 public final @NonNull Executor getCachedExecutor() { 872 synchronized (mExecutorLock) { 873 if (mExecutor == null) { 874 Executor e = getCreateExecutor(); 875 mExecutor = (e != null) ? e : Runnable::run; 876 } 877 return mExecutor; 878 } 879 } 880 881 /** 882 * Returns a string representation of the domain. 883 * @param domain The domain. 884 * @return The name of the domain. 885 * @hide 886 */ getDomainName(@etworkRegistrationInfo.Domain int domain)887 public static @NonNull String getDomainName(@NetworkRegistrationInfo.Domain int domain) { 888 return NetworkRegistrationInfo.domainToString(domain); 889 } 890 } 891