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