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