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 com.android.internal.telephony.domainselection;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.AsyncResult;
22 import android.os.Handler;
23 import android.os.Looper;
24 import android.os.Message;
25 import android.os.RemoteException;
26 import android.telephony.AccessNetworkConstants;
27 import android.telephony.AccessNetworkConstants.AccessNetworkType;
28 import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
29 import android.telephony.AccessNetworkConstants.TransportType;
30 import android.telephony.Annotation.ApnType;
31 import android.telephony.Annotation.DisconnectCauses;
32 import android.telephony.DisconnectCause;
33 import android.telephony.DomainSelectionService;
34 import android.telephony.DomainSelectionService.EmergencyScanType;
35 import android.telephony.DomainSelector;
36 import android.telephony.EmergencyRegistrationResult;
37 import android.telephony.NetworkRegistrationInfo;
38 import android.telephony.PreciseDisconnectCause;
39 import android.telephony.data.ApnSetting;
40 import android.telephony.ims.ImsReasonInfo;
41 import android.util.LocalLog;
42 import android.util.Log;
43 
44 import com.android.internal.annotations.VisibleForTesting;
45 import com.android.internal.infra.AndroidFuture;
46 import com.android.internal.telephony.IDomainSelector;
47 import com.android.internal.telephony.ITransportSelectorCallback;
48 import com.android.internal.telephony.ITransportSelectorResultCallback;
49 import com.android.internal.telephony.IWwanSelectorCallback;
50 import com.android.internal.telephony.IWwanSelectorResultCallback;
51 import com.android.internal.telephony.Phone;
52 import com.android.internal.telephony.data.AccessNetworksManager.QualifiedNetworks;
53 import com.android.internal.telephony.util.TelephonyUtils;
54 
55 import java.io.PrintWriter;
56 import java.util.List;
57 import java.util.concurrent.CompletableFuture;
58 
59 
60 /**
61  * Manages the information of request and the callback binder.
62  */
63 public class DomainSelectionConnection {
64 
65     private static final boolean DBG = TelephonyUtils.IS_DEBUGGABLE;
66 
67     protected static final int EVENT_EMERGENCY_NETWORK_SCAN_RESULT = 1;
68     protected static final int EVENT_QUALIFIED_NETWORKS_CHANGED = 2;
69     protected static final int EVENT_SERVICE_CONNECTED = 3;
70     protected static final int EVENT_SERVICE_BINDING_TIMEOUT = 4;
71     protected static final int EVENT_RESET_NETWORK_SCAN_DONE = 5;
72     protected static final int EVENT_LAST = EVENT_RESET_NETWORK_SCAN_DONE;
73 
74     private static final int DEFAULT_BIND_RETRY_TIMEOUT_MS = 4 * 1000;
75 
76     private static final int STATUS_DISPOSED         = 1 << 0;
77     private static final int STATUS_DOMAIN_SELECTED  = 1 << 1;
78     private static final int STATUS_WAIT_BINDING     = 1 << 2;
79     private static final int STATUS_WAIT_SCAN_RESULT = 1 << 3;
80     private static final int STATUS_WAIT_RESET_SCAN_RESULT = 1 << 4;
81 
82     /** Callback to receive responses from DomainSelectionConnection. */
83     public interface DomainSelectionConnectionCallback {
84         /**
85          * Notifies that selection has terminated because there is no decision that can be made
86          * or a timeout has occurred. The call should be terminated when this method is called.
87          *
88          * @param cause Indicates the reason.
89          */
onSelectionTerminated(@isconnectCauses int cause)90         void onSelectionTerminated(@DisconnectCauses int cause);
91     }
92 
93     private static class ScanRequest {
94         final int[] mPreferredNetworks;
95         final int mScanType;
96 
ScanRequest(int[] preferredNetworks, int scanType)97         ScanRequest(int[] preferredNetworks, int scanType) {
98             mPreferredNetworks = preferredNetworks;
99             mScanType = scanType;
100         }
101     }
102 
103     /**
104      * A wrapper class for {@link ITransportSelectorCallback} interface.
105      */
106     private final class TransportSelectorCallbackAdaptor extends ITransportSelectorCallback.Stub {
107         @Override
onCreated(@onNull IDomainSelector selector)108         public void onCreated(@NonNull IDomainSelector selector) {
109             synchronized (mLock) {
110                 mDomainSelector = selector;
111                 if (checkState(STATUS_DISPOSED)) {
112                     try {
113                         selector.finishSelection();
114                     } catch (RemoteException e) {
115                         // ignore exception
116                     }
117                     return;
118                 }
119                 DomainSelectionConnection.this.onCreated();
120             }
121         }
122 
123         @Override
onWlanSelected(boolean useEmergencyPdn)124         public void onWlanSelected(boolean useEmergencyPdn) {
125             synchronized (mLock) {
126                 if (checkState(STATUS_DISPOSED)) {
127                     return;
128                 }
129                 setState(STATUS_DOMAIN_SELECTED);
130                 DomainSelectionConnection.this.onWlanSelected(useEmergencyPdn);
131             }
132         }
133 
134         @Override
onWwanSelectedAsync(@onNull final ITransportSelectorResultCallback cb)135         public void onWwanSelectedAsync(@NonNull final ITransportSelectorResultCallback cb) {
136             synchronized (mLock) {
137                 if (checkState(STATUS_DISPOSED)) {
138                     return;
139                 }
140                 if (mWwanSelectorCallback == null) {
141                     mWwanSelectorCallback = new WwanSelectorCallbackAdaptor();
142                 }
143                 if (mIsTestMode || !mIsEmergency
144                         || (mSelectorType != DomainSelectionService.SELECTOR_TYPE_CALLING)) {
145                     initHandler();
146                     mHandler.post(() -> {
147                         onWwanSelectedAsyncInternal(cb);
148                     });
149                 } else {
150                     Thread workerThread = new Thread(new Runnable() {
151                         @Override
152                         public void run() {
153                             onWwanSelectedAsyncInternal(cb);
154                         }
155                     });
156                     workerThread.start();
157                 }
158             }
159         }
160 
onWwanSelectedAsyncInternal( @onNull final ITransportSelectorResultCallback cb)161         private void onWwanSelectedAsyncInternal(
162                 @NonNull final ITransportSelectorResultCallback cb) {
163             synchronized (mLock) {
164                 if (checkState(STATUS_DISPOSED)) {
165                     return;
166                 }
167             }
168             DomainSelectionConnection.this.onWwanSelected();
169             try {
170                 cb.onCompleted(mWwanSelectorCallback);
171             } catch (RemoteException e) {
172                 loge("onWwanSelectedAsync executor exception=" + e);
173                 synchronized (mLock) {
174                     // Since remote service is not available,
175                     // wait for binding or timeout.
176                     waitForServiceBinding(null);
177                 }
178             }
179         }
180 
181         @Override
onSelectionTerminated(int cause)182         public void onSelectionTerminated(int cause) {
183             synchronized (mLock) {
184                 if (checkState(STATUS_DISPOSED)) {
185                     return;
186                 }
187                 DomainSelectionConnection.this.onSelectionTerminated(cause);
188                 dispose();
189             }
190         }
191     }
192 
193     /**
194      * A wrapper class for {@link IWwanSelectorCallback} interface.
195      */
196     private final class WwanSelectorCallbackAdaptor extends IWwanSelectorCallback.Stub {
197         @Override
onRequestEmergencyNetworkScan( @onNull @adioAccessNetworkType int[] preferredNetworks, @EmergencyScanType int scanType, boolean resetScan, @NonNull IWwanSelectorResultCallback cb)198         public void onRequestEmergencyNetworkScan(
199                 @NonNull @RadioAccessNetworkType int[] preferredNetworks,
200                 @EmergencyScanType int scanType, boolean resetScan,
201                 @NonNull IWwanSelectorResultCallback cb) {
202             synchronized (mLock) {
203                 if (checkState(STATUS_DISPOSED)) {
204                     return;
205                 }
206                 mResultCallback = cb;
207                 initHandler();
208                 mHandler.post(() -> {
209                     synchronized (mLock) {
210                         DomainSelectionConnection.this.onRequestEmergencyNetworkScan(
211                                 preferredNetworks, scanType, resetScan);
212                     }
213                 });
214             }
215         }
216 
217         @Override
onDomainSelected(@etworkRegistrationInfo.Domain int domain, boolean useEmergencyPdn)218         public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain,
219                 boolean useEmergencyPdn) {
220             synchronized (mLock) {
221                 if (checkState(STATUS_DISPOSED)) {
222                     return;
223                 }
224                 setState(STATUS_DOMAIN_SELECTED);
225                 DomainSelectionConnection.this.onDomainSelected(domain, useEmergencyPdn);
226             }
227         }
228 
229         @Override
onCancel()230         public void onCancel() {
231             synchronized (mLock) {
232                 if (checkState(STATUS_DISPOSED) || mHandler == null) {
233                     return;
234                 }
235                 mHandler.post(() -> {
236                     DomainSelectionConnection.this.onCancel();
237                 });
238             }
239         }
240     }
241 
242     protected final class DomainSelectionConnectionHandler extends Handler {
DomainSelectionConnectionHandler(Looper looper)243         DomainSelectionConnectionHandler(Looper looper) {
244             super(looper);
245         }
246 
247         @Override
handleMessage(Message msg)248         public void handleMessage(Message msg) {
249             AsyncResult ar;
250             switch (msg.what) {
251                 case EVENT_EMERGENCY_NETWORK_SCAN_RESULT:
252                     ar = (AsyncResult) msg.obj;
253                     EmergencyRegistrationResult regResult = (EmergencyRegistrationResult) ar.result;
254                     if (DBG) logd("EVENT_EMERGENCY_NETWORK_SCAN_RESULT result=" + regResult);
255                     synchronized (mLock) {
256                         clearState(STATUS_WAIT_SCAN_RESULT);
257                         if (mResultCallback != null) {
258                             try {
259                                 mResultCallback.onComplete(regResult);
260                             } catch (RemoteException e) {
261                                 loge("EVENT_EMERGENCY_NETWORK_SCAN_RESULT exception=" + e);
262                                 // Since remote service is not available,
263                                 // wait for binding or timeout.
264                                 waitForServiceBinding(null);
265                             }
266                         }
267                     }
268                     break;
269                 case EVENT_QUALIFIED_NETWORKS_CHANGED:
270                     ar = (AsyncResult) msg.obj;
271                     if (ar == null || ar.result == null) {
272                         loge("handleMessage EVENT_QUALIFIED_NETWORKS_CHANGED null result");
273                         break;
274                     }
275                     onQualifiedNetworksChanged((List<QualifiedNetworks>) ar.result);
276                     break;
277                 case EVENT_SERVICE_CONNECTED:
278                     synchronized (mLock) {
279                         if (checkState(STATUS_DISPOSED) || !checkState(STATUS_WAIT_BINDING)) {
280                             loge("EVENT_SERVICE_CONNECTED disposed or not waiting for binding");
281                             break;
282                         }
283                         if (mController.selectDomain(mSelectionAttributes,
284                                 mTransportSelectorCallback)) {
285                             clearWaitingForServiceBinding();
286                         }
287                     }
288                     break;
289                 case EVENT_SERVICE_BINDING_TIMEOUT:
290                     synchronized (mLock) {
291                         if (!checkState(STATUS_DISPOSED) && checkState(STATUS_WAIT_BINDING)) {
292                             onServiceBindingTimeout();
293                         }
294                     }
295                     break;
296                 case EVENT_RESET_NETWORK_SCAN_DONE:
297                     synchronized (mLock) {
298                         clearState(STATUS_WAIT_RESET_SCAN_RESULT);
299                         if (checkState(STATUS_DISPOSED)
300                                 || (mPendingScanRequest == null)) {
301                             return;
302                         }
303                         onRequestEmergencyNetworkScan(mPendingScanRequest.mPreferredNetworks,
304                                 mPendingScanRequest.mScanType, false);
305                     }
306                     break;
307                 default:
308                     loge("handleMessage unexpected msg=" + msg.what);
309                     break;
310             }
311         }
312     }
313 
314     protected String mTag = "DomainSelectionConnection";
315 
316     private final Object mLock = new Object();
317     private final LocalLog mLocalLog = new LocalLog(30);
318     private final @NonNull ITransportSelectorCallback mTransportSelectorCallback;
319 
320     /**
321      * Controls the communication between {@link DomainSelectionConnection} and
322      * {@link DomainSelectionService}.
323      */
324     private final @NonNull DomainSelectionController mController;
325     /** Indicates whether the requested service is for emergency services. */
326     private final boolean mIsEmergency;
327 
328     /** Interface to receive the request to trigger emergency network scan and selected domain. */
329     private @Nullable IWwanSelectorCallback mWwanSelectorCallback;
330     /** Interface to return the result of emergency network scan. */
331     private @Nullable IWwanSelectorResultCallback mResultCallback;
332     /** Interface to the {@link DomainSelector} created for this service. */
333     private @Nullable IDomainSelector mDomainSelector;
334 
335     /** The bit-wise OR of STATUS_* values. */
336     private int mStatus;
337 
338     /** The slot requested this connection. */
339     protected @NonNull Phone mPhone;
340     /** The requested domain selector type. */
341     private @DomainSelectionService.SelectorType int mSelectorType;
342 
343     /** The attributes required to determine the domain. */
344     private @Nullable DomainSelectionService.SelectionAttributes mSelectionAttributes;
345 
346     private final @NonNull Looper mLooper;
347     protected @Nullable DomainSelectionConnectionHandler mHandler;
348     private boolean mRegisteredRegistrant;
349 
350     private @NonNull AndroidFuture<Integer> mOnComplete;
351 
352     private @Nullable ScanRequest mPendingScanRequest;
353 
354     private boolean mIsTestMode = false;
355 
356     private int mDisconnectCause = DisconnectCause.NOT_VALID;
357     private int mPreciseDisconnectCause = PreciseDisconnectCause.NOT_VALID;
358     private String mReasonMessage = null;
359 
360     /**
361      * Creates an instance.
362      *
363      * @param phone For which this service is requested.
364      * @param selectorType Indicates the type of the requested service.
365      * @param isEmergency Indicates whether this request is for emergency service.
366      * @param controller The controller to communicate with the domain selection service.
367      */
DomainSelectionConnection(@onNull Phone phone, @DomainSelectionService.SelectorType int selectorType, boolean isEmergency, @NonNull DomainSelectionController controller)368     public DomainSelectionConnection(@NonNull Phone phone,
369             @DomainSelectionService.SelectorType int selectorType, boolean isEmergency,
370             @NonNull DomainSelectionController controller) {
371         mController = controller;
372         mPhone = phone;
373         mSelectorType = selectorType;
374         mIsEmergency = isEmergency;
375         mLooper = Looper.getMainLooper();
376 
377         mTransportSelectorCallback = new TransportSelectorCallbackAdaptor();
378         mOnComplete = new AndroidFuture<>();
379     }
380 
381     /**
382      * Returns the attributes required to determine the domain for a telephony service.
383      *
384      * @return The attributes required to determine the domain.
385      */
getSelectionAttributes()386     public @Nullable DomainSelectionService.SelectionAttributes getSelectionAttributes() {
387         return mSelectionAttributes;
388     }
389 
390     /**
391      * Returns the callback binder interface.
392      *
393      * @return The {@link ITransportSelectorCallback} interface.
394      */
getTransportSelectorCallback()395     public @Nullable ITransportSelectorCallback getTransportSelectorCallback() {
396         return mTransportSelectorCallback;
397     }
398 
399     /**
400      * Returns the callback binder interface to handle the emergency scan result.
401      *
402      * @return The {@link IWwanSelectorResultCallback} interface.
403      */
getWwanSelectorResultCallback()404     public @Nullable IWwanSelectorResultCallback getWwanSelectorResultCallback() {
405         return mResultCallback;
406     }
407 
408     /**
409      * Returns the {@link CompletableFuture} to receive the selected domain.
410      *
411      * @return The callback to receive response.
412      */
getCompletableFuture()413     public @NonNull CompletableFuture<Integer> getCompletableFuture() {
414         return mOnComplete;
415     }
416 
417     /**
418      * Returs the {@link Phone} which requested this connection.
419      *
420      * @return The {@link Phone} instance.
421      */
getPhone()422     public @NonNull Phone getPhone() {
423         return mPhone;
424     }
425 
426     /**
427      * Requests the domain selection service to select a domain.
428      *
429      * @param attr The attributes required to determine the domain.
430      */
431     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
selectDomain(@onNull DomainSelectionService.SelectionAttributes attr)432     public void selectDomain(@NonNull DomainSelectionService.SelectionAttributes attr) {
433         synchronized (mLock) {
434             mSelectionAttributes = attr;
435             if (mController.selectDomain(attr, mTransportSelectorCallback)) {
436                 clearWaitingForServiceBinding();
437             } else {
438                 waitForServiceBinding(attr);
439             }
440         }
441     }
442 
443     /**
444      * Notifies that {@link DomainSelector} instance has been created for the selection request.
445      */
onCreated()446     public void onCreated() {
447         // Can be overridden if required
448     }
449 
450     /**
451      * Notifies that WLAN transport has been selected.
452      */
onWlanSelected()453     public void onWlanSelected() {
454         // Can be overridden.
455     }
456 
457     /**
458      * Notifies that WLAN transport has been selected.
459      *
460      * @param useEmergencyPdn Indicates whether Wi-Fi emergency services use emergency PDN or not.
461      */
onWlanSelected(boolean useEmergencyPdn)462     public void onWlanSelected(boolean useEmergencyPdn) {
463         // Can be overridden.
464         onWlanSelected();
465     }
466 
467     /**
468      * Notifies that WWAN transport has been selected.
469      */
onWwanSelected()470     public void onWwanSelected() {
471         // Can be overridden.
472     }
473 
474     /**
475      * Notifies that selection has terminated because there is no decision that can be made
476      * or a timeout has occurred. The call should be terminated when this method is called.
477      *
478      * @param cause Indicates the reason.
479      */
onSelectionTerminated(@isconnectCauses int cause)480     public void onSelectionTerminated(@DisconnectCauses int cause) {
481         // Can be overridden.
482     }
483 
484     /**
485      * Requests the emergency network scan.
486      *
487      * @param preferredNetworks The ordered list of preferred networks to scan.
488      * @param scanType Indicates the scan preference, such as full service or limited service.
489      * @param resetScan Indicates that the previous scan result shall be reset before scanning.
490      */
onRequestEmergencyNetworkScan( @onNull @adioAccessNetworkType int[] preferredNetworks, @EmergencyScanType int scanType, boolean resetScan)491     public void onRequestEmergencyNetworkScan(
492             @NonNull @RadioAccessNetworkType int[] preferredNetworks,
493             @EmergencyScanType int scanType, boolean resetScan) {
494         // Can be overridden if required
495 
496         synchronized (mLock) {
497             if (mHandler == null
498                     || checkState(STATUS_DISPOSED)
499                     || checkState(STATUS_WAIT_SCAN_RESULT)) {
500                 logi("onRequestEmergencyNetworkScan waitResult="
501                         + checkState(STATUS_WAIT_SCAN_RESULT));
502                 return;
503             }
504 
505             if (checkState(STATUS_WAIT_RESET_SCAN_RESULT)) {
506                 if (mPendingScanRequest != null) {
507                     /* Consecutive scan requests without cancellation is not an expected use case.
508                      * DomainSelector should cancel the previous request or wait for the result
509                      * before requesting a new scan.*/
510                     logi("onRequestEmergencyNetworkScan consecutive scan requests");
511                     return;
512                 } else {
513                     // The reset has not been completed.
514                     // case1) Long delay in cancelEmergencyNetworkScan by modem.
515                     // case2) A consecutive scan requests with short interval from DomainSelector.
516                     logi("onRequestEmergencyNetworkScan reset not completed");
517                 }
518                 mPendingScanRequest = new ScanRequest(preferredNetworks, scanType);
519                 return;
520             } else if (resetScan) {
521                 setState(STATUS_WAIT_RESET_SCAN_RESULT);
522                 mPendingScanRequest = new ScanRequest(preferredNetworks, scanType);
523                 mPhone.cancelEmergencyNetworkScan(resetScan,
524                         mHandler.obtainMessage(EVENT_RESET_NETWORK_SCAN_DONE));
525                 return;
526             }
527 
528             if (!mRegisteredRegistrant) {
529                 mPhone.registerForEmergencyNetworkScan(mHandler,
530                         EVENT_EMERGENCY_NETWORK_SCAN_RESULT, null);
531                 mRegisteredRegistrant = true;
532             }
533             setState(STATUS_WAIT_SCAN_RESULT);
534             mPhone.triggerEmergencyNetworkScan(preferredNetworks, scanType, null);
535             mPendingScanRequest = null;
536         }
537     }
538 
539     /**
540      * Notifies the domain selected.
541      *
542      * @param domain The selected domain.
543      */
onDomainSelected(@etworkRegistrationInfo.Domain int domain)544     public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain) {
545         // Can be overridden if required
546         CompletableFuture<Integer> future = getCompletableFuture();
547         future.complete(domain);
548     }
549 
550     /**
551      * Notifies the domain selected.
552      *
553      * @param domain The selected domain.
554      * @param useEmergencyPdn Indicates whether emergency services use emergency PDN or not.
555      */
onDomainSelected(@etworkRegistrationInfo.Domain int domain, boolean useEmergencyPdn)556     public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain,
557             boolean useEmergencyPdn) {
558         // Can be overridden if required
559         onDomainSelected(domain);
560     }
561 
562     /**
563      * Notifies that the emergency network scan is canceled.
564      */
onCancel()565     public void onCancel() {
566         // Can be overridden if required
567         onCancel(false);
568     }
569 
onCancel(boolean resetScan)570     private void onCancel(boolean resetScan) {
571         mPendingScanRequest = null;
572         if (checkState(STATUS_WAIT_SCAN_RESULT)) {
573             clearState(STATUS_WAIT_SCAN_RESULT);
574             mPhone.cancelEmergencyNetworkScan(resetScan, null);
575         }
576     }
577 
578     /**
579      * Cancels an ongoing selection operation. It is up to the {@link DomainSelectionService}
580      * to clean up all ongoing operations with the framework.
581      */
cancelSelection()582     public void cancelSelection() {
583         finishSelection();
584     }
585 
586     /**
587      * Requests the domain selection service to reselect a domain.
588      *
589      * @param attr The attributes required to determine the domain.
590      * @return The callback to receive the response.
591      */
reselectDomain( @onNull DomainSelectionService.SelectionAttributes attr)592     public @NonNull CompletableFuture<Integer> reselectDomain(
593             @NonNull DomainSelectionService.SelectionAttributes attr) {
594         synchronized (mLock) {
595             mSelectionAttributes = attr;
596             mOnComplete = new AndroidFuture<>();
597             clearState(STATUS_DOMAIN_SELECTED);
598             try {
599                 if (mDomainSelector == null) {
600                     // Service connection has been disconnected.
601                     mSelectionAttributes = getSelectionAttributesToRebindService();
602                     if (mController.selectDomain(mSelectionAttributes,
603                             mTransportSelectorCallback)) {
604                         clearWaitingForServiceBinding();
605                     } else {
606                         waitForServiceBinding(null);
607                     }
608                 } else {
609                     mDomainSelector.reselectDomain(attr);
610                 }
611             } catch (RemoteException e) {
612                 loge("reselectDomain exception=" + e);
613                 // Since remote service is not available, wait for binding or timeout.
614                 waitForServiceBinding(null);
615             } finally {
616                 return mOnComplete;
617             }
618         }
619     }
620 
621     /**
622      * Finishes the selection procedure and cleans everything up.
623      */
finishSelection()624     public void finishSelection() {
625         synchronized (mLock) {
626             try {
627                 if (mDomainSelector != null) {
628                     mDomainSelector.finishSelection();
629                 }
630             } catch (RemoteException e) {
631                 loge("finishSelection exception=" + e);
632             } finally {
633                 dispose();
634             }
635         }
636     }
637 
638     /** Indicates that the service connection has been connected. */
onServiceConnected()639     public void onServiceConnected() {
640         synchronized (mLock) {
641             if (checkState(STATUS_DISPOSED) || !checkState(STATUS_WAIT_BINDING)) {
642                 logi("onServiceConnected disposed or not waiting for the binding");
643                 return;
644             }
645             initHandler();
646             mHandler.sendEmptyMessage(EVENT_SERVICE_CONNECTED);
647         }
648     }
649 
650     /** Indicates that the service connection has been removed. */
onServiceDisconnected()651     public void onServiceDisconnected() {
652         synchronized (mLock) {
653             if (mHandler != null) {
654                 mHandler.removeMessages(EVENT_SERVICE_CONNECTED);
655             }
656             if (checkState(STATUS_DISPOSED) || checkState(STATUS_DOMAIN_SELECTED)) {
657                 // If there is an on-going dialing, recovery shall happen
658                 // when dialing fails and reselectDomain() is called.
659                 mDomainSelector = null;
660                 mResultCallback = null;
661                 return;
662             }
663             // Since remote service is not available, wait for binding or timeout.
664             waitForServiceBinding(null);
665         }
666     }
667 
waitForServiceBinding(DomainSelectionService.SelectionAttributes attr)668     private void waitForServiceBinding(DomainSelectionService.SelectionAttributes attr) {
669         if (checkState(STATUS_DISPOSED) || checkState(STATUS_WAIT_BINDING)) {
670             // Already done.
671             return;
672         }
673         setState(STATUS_WAIT_BINDING);
674         mDomainSelector = null;
675         mResultCallback = null;
676         mSelectionAttributes = (attr != null) ? attr : getSelectionAttributesToRebindService();
677         initHandler();
678         mHandler.sendEmptyMessageDelayed(EVENT_SERVICE_BINDING_TIMEOUT,
679                 DEFAULT_BIND_RETRY_TIMEOUT_MS);
680     }
681 
clearWaitingForServiceBinding()682     private void clearWaitingForServiceBinding() {
683         if (checkState(STATUS_WAIT_BINDING)) {
684             clearState(STATUS_WAIT_BINDING);
685             if (mHandler != null) {
686                 mHandler.removeMessages(EVENT_SERVICE_BINDING_TIMEOUT);
687             }
688         }
689     }
690 
onServiceBindingTimeout()691     protected void onServiceBindingTimeout() {
692         // Can be overridden if required
693         synchronized (mLock) {
694             if (checkState(STATUS_DISPOSED)) {
695                 logi("onServiceBindingTimeout disposed");
696                 return;
697             }
698             DomainSelectionConnection.this.onSelectionTerminated(
699                     getTerminationCauseForSelectionTimeout());
700             dispose();
701         }
702     }
703 
getTerminationCauseForSelectionTimeout()704     protected int getTerminationCauseForSelectionTimeout() {
705         // Can be overridden if required
706         return DisconnectCause.TIMED_OUT;
707     }
708 
709     protected DomainSelectionService.SelectionAttributes
getSelectionAttributesToRebindService()710             getSelectionAttributesToRebindService() {
711         // Can be overridden if required
712         return mSelectionAttributes;
713     }
714 
715     /** Returns whether the client is waiting for the service binding. */
isWaitingForServiceBinding()716     public boolean isWaitingForServiceBinding() {
717         return checkState(STATUS_WAIT_BINDING) && !checkState(STATUS_DISPOSED);
718     }
719 
dispose()720     private void dispose() {
721         setState(STATUS_DISPOSED);
722         if (mRegisteredRegistrant) {
723             mPhone.unregisterForEmergencyNetworkScan(mHandler);
724             mRegisteredRegistrant = false;
725         }
726         onCancel(true);
727         mController.removeConnection(this);
728         if (mHandler != null) mHandler.removeCallbacksAndMessages(null);
729         mHandler = null;
730     }
731 
initHandler()732     protected void initHandler() {
733         if (mHandler == null) mHandler = new DomainSelectionConnectionHandler(mLooper);
734     }
735 
736     /**
737      * Notifies the change of qualified networks.
738      */
onQualifiedNetworksChanged(List<QualifiedNetworks> networksList)739     protected void onQualifiedNetworksChanged(List<QualifiedNetworks> networksList) {
740         if (mIsEmergency
741                 && (mSelectorType == DomainSelectionService.SELECTOR_TYPE_CALLING)) {
742             // DomainSelectionConnection for emergency calls shall override this.
743             throw new IllegalStateException("DomainSelectionConnection for emergency calls"
744                     + " should override onQualifiedNetworksChanged()");
745         }
746     }
747 
748     /**
749      * Get the  preferred transport.
750      *
751      * @param apnType APN type.
752      * @return The preferred transport.
753      */
754     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
getPreferredTransport(@pnType int apnType, List<QualifiedNetworks> networksList)755     public int getPreferredTransport(@ApnType int apnType,
756             List<QualifiedNetworks> networksList) {
757         for (QualifiedNetworks networks : networksList) {
758             if (networks.qualifiedNetworks.length > 0) {
759                 if (networks.apnType == apnType) {
760                     return getTransportFromAccessNetwork(networks.qualifiedNetworks[0]);
761                 }
762             }
763         }
764 
765         loge("getPreferredTransport no network found for " + ApnSetting.getApnTypeString(apnType));
766         return AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
767     }
768 
getTransportFromAccessNetwork(int accessNetwork)769     private static @TransportType int getTransportFromAccessNetwork(int accessNetwork) {
770         return accessNetwork == AccessNetworkType.IWLAN
771                 ? AccessNetworkConstants.TRANSPORT_TYPE_WLAN
772                 : AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
773     }
774 
setState(int stateBit)775     private void setState(int stateBit) {
776         mStatus |= stateBit;
777     }
778 
clearState(int stateBit)779     private void clearState(int stateBit) {
780         mStatus &= ~stateBit;
781     }
782 
checkState(int stateBit)783     private boolean checkState(int stateBit) {
784         return (mStatus & stateBit) == stateBit;
785     }
786 
787     /**
788      * Set whether it is unit test or not.
789      *
790      * @param testMode Indicates whether it is unit test or not.
791      */
792     @VisibleForTesting
setTestMode(boolean testMode)793     public void setTestMode(boolean testMode) {
794         mIsTestMode = testMode;
795     }
796 
797     /**
798      * Save call disconnect info for error propagation.
799      * @param disconnectCause The code for the reason for the disconnect.
800      * @param preciseDisconnectCause The code for the precise reason for the disconnect.
801      * @param reasonMessage Description of the reason for the disconnect, not intended for the user
802      *                      to see.
803      */
setDisconnectCause(int disconnectCause, int preciseDisconnectCause, String reasonMessage)804     public void setDisconnectCause(int disconnectCause, int preciseDisconnectCause,
805                                 String reasonMessage) {
806         mDisconnectCause = disconnectCause;
807         mPreciseDisconnectCause = preciseDisconnectCause;
808         mReasonMessage = reasonMessage;
809     }
810 
getDisconnectCause()811     public int getDisconnectCause() {
812         return mDisconnectCause;
813     }
814 
getPreciseDisconnectCause()815     public int getPreciseDisconnectCause() {
816         return mPreciseDisconnectCause;
817     }
818 
getReasonMessage()819     public String getReasonMessage() {
820         return mReasonMessage;
821     }
822 
823     /**
824      * @return imsReasonInfo Reason for the IMS call failure.
825      */
getImsReasonInfo()826     public @Nullable ImsReasonInfo getImsReasonInfo() {
827         if (getSelectionAttributes() == null) {
828             // Neither selectDomain(...) nor reselectDomain(...) has been called yet.
829             return null;
830         }
831 
832         return getSelectionAttributes().getPsDisconnectCause();
833     }
834 
835     /**
836      * @return phoneId To support localized message based on phoneId
837      */
getPhoneId()838     public int getPhoneId() {
839         return getPhone().getPhoneId();
840     }
841 
842     /**
843      * Dumps local log.
844      */
dump(@onNull PrintWriter printWriter)845     public void dump(@NonNull PrintWriter printWriter) {
846         mLocalLog.dump(printWriter);
847     }
848 
logd(String msg)849     protected void logd(String msg) {
850         Log.d(mTag, msg);
851     }
852 
logi(String msg)853     protected void logi(String msg) {
854         Log.i(mTag, msg);
855         mLocalLog.log(msg);
856     }
857 
loge(String msg)858     protected void loge(String msg) {
859         Log.e(mTag, msg);
860         mLocalLog.log(msg);
861     }
862 }
863