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