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