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