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.services.telephony.domainselection; 18 19 import android.annotation.NonNull; 20 import android.content.Context; 21 import android.os.Handler; 22 import android.os.Looper; 23 import android.telephony.AccessNetworkConstants.AccessNetworkType; 24 import android.telephony.AccessNetworkConstants.RadioAccessNetworkType; 25 import android.telephony.BarringInfo; 26 import android.telephony.ServiceState; 27 import android.telephony.SubscriptionManager; 28 import android.telephony.ims.ImsException; 29 import android.telephony.ims.ImsManager; 30 import android.telephony.ims.ImsMmTelManager; 31 import android.telephony.ims.ImsReasonInfo; 32 import android.telephony.ims.ImsRegistrationAttributes; 33 import android.telephony.ims.ImsStateCallback; 34 import android.telephony.ims.ImsStateCallback.DisconnectedReason; 35 import android.telephony.ims.RegistrationManager; 36 import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities; 37 import android.telephony.ims.stub.ImsRegistrationImplBase; 38 import android.util.ArraySet; 39 import android.util.IndentingPrintWriter; 40 import android.util.LocalLog; 41 import android.util.Log; 42 43 import com.android.internal.annotations.Keep; 44 import com.android.internal.annotations.VisibleForTesting; 45 46 import java.io.PrintWriter; 47 import java.util.Objects; 48 import java.util.Set; 49 50 /** 51 * A class for tracking the IMS related information like IMS registration state, MMTEL capabilities. 52 * And, it also tracks the {@link ServiceState} and {@link BarringInfo} to identify the current 53 * network state to which the device is attached. 54 */ 55 @Keep 56 public class ImsStateTracker { 57 /** 58 * A listener used to be notified of the {@link ServiceState} change. 59 */ 60 public interface ServiceStateListener { 61 /** 62 * Called when the {@link ServiceState} is updated. 63 */ onServiceStateUpdated(ServiceState serviceState)64 void onServiceStateUpdated(ServiceState serviceState); 65 } 66 67 /** 68 * A listener used to be notified of the {@link BarringInfo} change. 69 */ 70 public interface BarringInfoListener { 71 /** 72 * Called when the {@link BarringInfo} is updated. 73 */ onBarringInfoUpdated(BarringInfo barringInfo)74 void onBarringInfoUpdated(BarringInfo barringInfo); 75 } 76 77 /** 78 * A listener used to be notified of the change for MMTEL connection state, IMS registration 79 * state, and MMTEL capabilities. 80 */ 81 public interface ImsStateListener { 82 /** 83 * Called when MMTEL feature connection state is changed. 84 */ onImsMmTelFeatureAvailableChanged()85 void onImsMmTelFeatureAvailableChanged(); 86 87 /** 88 * Called when IMS registration state is changed. 89 */ onImsRegistrationStateChanged()90 void onImsRegistrationStateChanged(); 91 92 /** 93 * Called when MMTEL capability is changed - IMS is registered 94 * and the service is currently available over IMS. 95 */ onImsMmTelCapabilitiesChanged()96 void onImsMmTelCapabilitiesChanged(); 97 } 98 99 private static final String TAG = ImsStateTracker.class.getSimpleName(); 100 /** 101 * When MMTEL feature connection is unavailable temporarily, 102 * the IMS state will be set to unavailable after waiting for this time. 103 */ 104 @VisibleForTesting 105 protected static final long MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS = 1000; // 1 seconds 106 107 // Persistent Logging 108 private final LocalLog mEventLog = new LocalLog(30); 109 private final Context mContext; 110 private final int mSlotId; 111 private final Handler mHandler; 112 private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 113 114 /** For tracking the ServiceState and its related listeners. */ 115 private ServiceState mServiceState; 116 private final Set<ServiceStateListener> mServiceStateListeners = new ArraySet<>(2); 117 118 /** For tracking the BarringInfo and its related listeners. */ 119 private BarringInfo mBarringInfo; 120 private final Set<BarringInfoListener> mBarringInfoListeners = new ArraySet<>(2); 121 122 /** For tracking IMS states and callbacks. */ 123 private final Set<ImsStateListener> mImsStateListeners = new ArraySet<>(5); 124 private ImsMmTelManager mMmTelManager; 125 private ImsStateCallback mImsStateCallback; 126 private RegistrationManager.RegistrationCallback mImsRegistrationCallback; 127 private ImsMmTelManager.CapabilityCallback mMmTelCapabilityCallback; 128 /** The availability of MmTelFeature. */ 129 private Boolean mMmTelFeatureAvailable; 130 /** The IMS registration state and the network type that performed IMS registration. */ 131 private Boolean mImsRegistered; 132 private @RadioAccessNetworkType int mImsAccessNetworkType = AccessNetworkType.UNKNOWN; 133 private Boolean mImsRegisteredOverCrossSim; 134 /** The MMTEL capabilities - Voice, Video, SMS, and Ut. */ 135 private MmTelCapabilities mMmTelCapabilities; 136 private final Runnable mMmTelFeatureUnavailableRunnable = new Runnable() { 137 @Override 138 public void run() { 139 setImsStateAsUnavailable(); 140 notifyImsMmTelFeatureAvailableChanged(); 141 } 142 }; 143 ImsStateTracker(@onNull Context context, int slotId, @NonNull Looper looper)144 public ImsStateTracker(@NonNull Context context, int slotId, @NonNull Looper looper) { 145 mContext = context; 146 mSlotId = slotId; 147 mHandler = new Handler(looper); 148 } 149 150 /** 151 * Destroys this tracker. 152 */ destroy()153 public void destroy() { 154 stopListeningForImsState(); 155 mHandler.removeCallbacksAndMessages(null); 156 } 157 158 /** 159 * Returns the slot index for this tracker. 160 */ getSlotId()161 public int getSlotId() { 162 return mSlotId; 163 } 164 165 /** 166 * Returns the current subscription index for this tracker. 167 */ getSubId()168 public int getSubId() { 169 return mSubId; 170 } 171 172 /** 173 * Returns the Handler instance of this tracker. 174 */ 175 @VisibleForTesting getHandler()176 public @NonNull Handler getHandler() { 177 return mHandler; 178 } 179 180 /** 181 * Starts monitoring the IMS states with the specified subscription. 182 * This method will be called whenever the subscription index for this tracker is changed. 183 * If the subscription index for this tracker is same as previously set, it will be ignored. 184 * 185 * @param subId The subscription index to be started. 186 */ start(int subId)187 public void start(int subId) { 188 if (mSubId == subId) { 189 if (!SubscriptionManager.isValidSubscriptionId(mSubId)) { 190 setImsStateAsUnavailable(); 191 return; 192 } else if (mImsStateCallback != null) { 193 // If start() is called with the same subscription index and the ImsStateCallback 194 // was already registered, we don't need to unregister and register this callback 195 // again. So, this request should be ignored if the subscription index is same. 196 logd("start: ignored for same subscription(" + mSubId + ")"); 197 return; 198 } 199 } else { 200 logi("start: subscription changed from " + mSubId + " to " + subId); 201 mSubId = subId; 202 } 203 204 stopListeningForImsState(); 205 startListeningForImsState(); 206 } 207 208 /** 209 * Updates the service state of the network to which the device is currently attached. 210 * This method should be run on the same thread as the Handler. 211 * 212 * @param serviceState The {@link ServiceState} to be updated. 213 */ updateServiceState(ServiceState serviceState)214 public void updateServiceState(ServiceState serviceState) { 215 mServiceState = serviceState; 216 217 for (ServiceStateListener listener : mServiceStateListeners) { 218 listener.onServiceStateUpdated(serviceState); 219 } 220 } 221 222 /** 223 * Adds a listener to be notified of the {@link ServiceState} change. 224 * The newly added listener is notified if the current {@link ServiceState} is present. 225 * 226 * @param listener The listener to be added. 227 */ addServiceStateListener(@onNull ServiceStateListener listener)228 public void addServiceStateListener(@NonNull ServiceStateListener listener) { 229 mServiceStateListeners.add(listener); 230 231 final ServiceState serviceState = mServiceState; 232 if (serviceState != null) { 233 mHandler.post(() -> notifyServiceStateUpdated(listener, serviceState)); 234 } 235 } 236 237 /** 238 * Removes a listener to be notified of the {@link ServiceState} change. 239 * 240 * @param listener The listener to be removed. 241 */ removeServiceStateListener(@onNull ServiceStateListener listener)242 public void removeServiceStateListener(@NonNull ServiceStateListener listener) { 243 mServiceStateListeners.remove(listener); 244 } 245 246 /** 247 * Notifies the specified listener of a change to {@link ServiceState}. 248 * 249 * @param listener The listener to be notified. 250 * @param serviceState The {@link ServiceState} to be reported. 251 */ notifyServiceStateUpdated(ServiceStateListener listener, ServiceState serviceState)252 private void notifyServiceStateUpdated(ServiceStateListener listener, 253 ServiceState serviceState) { 254 if (!mServiceStateListeners.contains(listener)) { 255 return; 256 } 257 listener.onServiceStateUpdated(serviceState); 258 } 259 260 /** 261 * Updates the barring information received from the network to which the device is currently 262 * attached. 263 * This method should be run on the same thread as the Handler. 264 * 265 * @param barringInfo The {@link BarringInfo} to be updated. 266 */ updateBarringInfo(BarringInfo barringInfo)267 public void updateBarringInfo(BarringInfo barringInfo) { 268 mBarringInfo = barringInfo; 269 270 for (BarringInfoListener listener : mBarringInfoListeners) { 271 listener.onBarringInfoUpdated(barringInfo); 272 } 273 } 274 275 /** 276 * Adds a listener to be notified of the {@link BarringInfo} change. 277 * The newly added listener is notified if the current {@link BarringInfo} is present. 278 * 279 * @param listener The listener to be added. 280 */ addBarringInfoListener(@onNull BarringInfoListener listener)281 public void addBarringInfoListener(@NonNull BarringInfoListener listener) { 282 mBarringInfoListeners.add(listener); 283 284 final BarringInfo barringInfo = mBarringInfo; 285 if (barringInfo != null) { 286 mHandler.post(() -> notifyBarringInfoUpdated(listener, barringInfo)); 287 } 288 } 289 290 /** 291 * Removes a listener to be notified of the {@link BarringInfo} change. 292 * 293 * @param listener The listener to be removed. 294 */ removeBarringInfoListener(@onNull BarringInfoListener listener)295 public void removeBarringInfoListener(@NonNull BarringInfoListener listener) { 296 mBarringInfoListeners.remove(listener); 297 } 298 299 /** 300 * Notifies the specified listener of a change to {@link BarringInfo}. 301 * 302 * @param listener The listener to be notified. 303 * @param barringInfo The {@link BarringInfo} to be reported. 304 */ notifyBarringInfoUpdated(BarringInfoListener listener, BarringInfo barringInfo)305 private void notifyBarringInfoUpdated(BarringInfoListener listener, BarringInfo barringInfo) { 306 if (!mBarringInfoListeners.contains(listener)) { 307 return; 308 } 309 listener.onBarringInfoUpdated(barringInfo); 310 } 311 312 /** 313 * Adds a listener to be notified of the IMS state change. 314 * If each state was already received from the IMS service, the newly added listener 315 * is notified once. 316 * 317 * @param listener The listener to be added. 318 */ addImsStateListener(@onNull ImsStateListener listener)319 public void addImsStateListener(@NonNull ImsStateListener listener) { 320 mImsStateListeners.add(listener); 321 mHandler.post(() -> notifyImsStateChangeIfValid(listener)); 322 } 323 324 /** 325 * Removes a listener to be notified of the IMS state change. 326 * 327 * @param listener The listener to be removed. 328 */ removeImsStateListener(@onNull ImsStateListener listener)329 public void removeImsStateListener(@NonNull ImsStateListener listener) { 330 mImsStateListeners.remove(listener); 331 } 332 333 /** 334 * Returns {@code true} if all IMS states are ready, {@code false} otherwise. 335 */ 336 @VisibleForTesting isImsStateReady()337 public boolean isImsStateReady() { 338 return mMmTelFeatureAvailable != null 339 && mImsRegistered != null 340 && mMmTelCapabilities != null; 341 } 342 343 /** 344 * Returns {@code true} if MMTEL feature connection is available, {@code false} otherwise. 345 */ isMmTelFeatureAvailable()346 public boolean isMmTelFeatureAvailable() { 347 return mMmTelFeatureAvailable != null && mMmTelFeatureAvailable; 348 } 349 350 /** 351 * Returns {@code true} if IMS is registered, {@code false} otherwise. 352 */ isImsRegistered()353 public boolean isImsRegistered() { 354 return mImsRegistered != null && mImsRegistered; 355 } 356 357 /** 358 * Returns {@code true} if IMS is registered over Wi-Fi (IWLAN), {@code false} otherwise. 359 */ isImsRegisteredOverWlan()360 public boolean isImsRegisteredOverWlan() { 361 return mImsAccessNetworkType == AccessNetworkType.IWLAN; 362 } 363 364 /** 365 * Returns {@code true} if IMS is registered over the mobile data of another subscription. 366 */ isImsRegisteredOverCrossSim()367 public boolean isImsRegisteredOverCrossSim() { 368 return mImsRegisteredOverCrossSim != null && mImsRegisteredOverCrossSim; 369 } 370 371 /** 372 * Returns {@code true} if IMS voice call is capable, {@code false} otherwise. 373 */ isImsVoiceCapable()374 public boolean isImsVoiceCapable() { 375 return mMmTelCapabilities != null 376 && mMmTelCapabilities.isCapable(MmTelCapabilities.CAPABILITY_TYPE_VOICE); 377 } 378 379 /** 380 * Returns {@code true} if IMS video call is capable, {@code false} otherwise. 381 */ isImsVideoCapable()382 public boolean isImsVideoCapable() { 383 return mMmTelCapabilities != null 384 && mMmTelCapabilities.isCapable(MmTelCapabilities.CAPABILITY_TYPE_VIDEO); 385 } 386 387 /** 388 * Returns {@code true} if IMS SMS is capable, {@code false} otherwise. 389 */ isImsSmsCapable()390 public boolean isImsSmsCapable() { 391 return mMmTelCapabilities != null 392 && mMmTelCapabilities.isCapable(MmTelCapabilities.CAPABILITY_TYPE_SMS); 393 } 394 395 /** 396 * Returns {@code true} if IMS UT is capable, {@code false} otherwise. 397 */ isImsUtCapable()398 public boolean isImsUtCapable() { 399 return mMmTelCapabilities != null 400 && mMmTelCapabilities.isCapable(MmTelCapabilities.CAPABILITY_TYPE_UT); 401 } 402 403 /** 404 * Returns the access network type to which IMS is registered. 405 */ getImsAccessNetworkType()406 public @RadioAccessNetworkType int getImsAccessNetworkType() { 407 return mImsAccessNetworkType; 408 } 409 410 /** 411 * Sets the IMS states to the initial values. 412 */ initImsState()413 private void initImsState() { 414 mMmTelFeatureAvailable = null; 415 mImsRegistered = null; 416 mImsAccessNetworkType = AccessNetworkType.UNKNOWN; 417 mImsRegisteredOverCrossSim = null; 418 mMmTelCapabilities = null; 419 } 420 421 /** 422 * Sets the IMS states to unavailable to notify the readiness of the IMS state 423 * when the subscription is not valid. 424 */ setImsStateAsUnavailable()425 private void setImsStateAsUnavailable() { 426 logd("setImsStateAsUnavailable"); 427 setMmTelFeatureAvailable(false); 428 setImsRegistered(false); 429 setImsAccessNetworkType(AccessNetworkType.UNKNOWN); 430 setImsRegisteredOverCrossSim(false); 431 setMmTelCapabilities(new MmTelCapabilities()); 432 } 433 setMmTelFeatureAvailable(boolean available)434 private void setMmTelFeatureAvailable(boolean available) { 435 if (!Objects.equals(mMmTelFeatureAvailable, Boolean.valueOf(available))) { 436 logi("setMmTelFeatureAvailable: " + mMmTelFeatureAvailable + " >> " + available); 437 mMmTelFeatureAvailable = Boolean.valueOf(available); 438 } 439 } 440 setImsRegistered(boolean registered)441 private void setImsRegistered(boolean registered) { 442 if (!Objects.equals(mImsRegistered, Boolean.valueOf(registered))) { 443 logi("setImsRegistered: " + mImsRegistered + " >> " + registered); 444 mImsRegistered = Boolean.valueOf(registered); 445 } 446 } 447 setImsAccessNetworkType(int accessNetworkType)448 private void setImsAccessNetworkType(int accessNetworkType) { 449 if (mImsAccessNetworkType != accessNetworkType) { 450 logi("setImsAccessNetworkType: " + accessNetworkTypeToString(mImsAccessNetworkType) 451 + " >> " + accessNetworkTypeToString(accessNetworkType)); 452 mImsAccessNetworkType = accessNetworkType; 453 } 454 } 455 setMmTelCapabilities(@onNull MmTelCapabilities capabilities)456 private void setMmTelCapabilities(@NonNull MmTelCapabilities capabilities) { 457 if (!Objects.equals(mMmTelCapabilities, capabilities)) { 458 logi("MMTEL capabilities: " + mMmTelCapabilities + " >> " + capabilities); 459 mMmTelCapabilities = capabilities; 460 } 461 } 462 setImsRegisteredOverCrossSim(boolean crossSim)463 private void setImsRegisteredOverCrossSim(boolean crossSim) { 464 if (!Objects.equals(mImsRegisteredOverCrossSim, Boolean.valueOf(crossSim))) { 465 logi("setImsRegisteredOverCrossSim: " + mImsRegisteredOverCrossSim + " >> " + crossSim); 466 mImsRegisteredOverCrossSim = Boolean.valueOf(crossSim); 467 } 468 } 469 470 /** 471 * Notifies the specified listener of the current IMS state if it's valid. 472 * 473 * @param listener The {@link ImsStateListener} to be notified. 474 */ notifyImsStateChangeIfValid(@onNull ImsStateListener listener)475 private void notifyImsStateChangeIfValid(@NonNull ImsStateListener listener) { 476 if (!mImsStateListeners.contains(listener)) { 477 return; 478 } 479 480 if (mMmTelFeatureAvailable != null) { 481 listener.onImsMmTelFeatureAvailableChanged(); 482 } 483 484 if (mImsRegistered != null) { 485 listener.onImsRegistrationStateChanged(); 486 } 487 488 if (mMmTelCapabilities != null) { 489 listener.onImsMmTelCapabilitiesChanged(); 490 } 491 } 492 493 /** 494 * Notifies the application that MMTEL feature connection state is changed. 495 */ notifyImsMmTelFeatureAvailableChanged()496 private void notifyImsMmTelFeatureAvailableChanged() { 497 for (ImsStateListener l : mImsStateListeners) { 498 l.onImsMmTelFeatureAvailableChanged(); 499 } 500 } 501 502 /** 503 * Notifies the application that IMS registration state is changed. 504 */ notifyImsRegistrationStateChanged()505 private void notifyImsRegistrationStateChanged() { 506 logi("ImsState: " + imsStateToString()); 507 for (ImsStateListener l : mImsStateListeners) { 508 l.onImsRegistrationStateChanged(); 509 } 510 } 511 512 /** 513 * Notifies the application that MMTEL capabilities is changed. 514 */ notifyImsMmTelCapabilitiesChanged()515 private void notifyImsMmTelCapabilitiesChanged() { 516 logi("ImsState: " + imsStateToString()); 517 for (ImsStateListener l : mImsStateListeners) { 518 l.onImsMmTelCapabilitiesChanged(); 519 } 520 } 521 522 /** 523 * Called when MMTEL feature connection state is available. 524 */ onMmTelFeatureAvailable()525 private void onMmTelFeatureAvailable() { 526 logd("onMmTelFeatureAvailable"); 527 mHandler.removeCallbacks(mMmTelFeatureUnavailableRunnable); 528 setMmTelFeatureAvailable(true); 529 registerImsRegistrationCallback(); 530 registerMmTelCapabilityCallback(); 531 notifyImsMmTelFeatureAvailableChanged(); 532 } 533 534 /** 535 * Called when MMTEL feature connection state is unavailable. 536 */ onMmTelFeatureUnavailable(@isconnectedReason int reason)537 private void onMmTelFeatureUnavailable(@DisconnectedReason int reason) { 538 logd("onMmTelFeatureUnavailable: reason=" + disconnectedCauseToString(reason)); 539 540 if (reason == ImsStateCallback.REASON_UNKNOWN_TEMPORARY_ERROR 541 || reason == ImsStateCallback.REASON_IMS_SERVICE_NOT_READY) { 542 // Wait for onAvailable for some times and 543 // if it's not available, the IMS state will be set to unavailable. 544 initImsState(); 545 setMmTelFeatureAvailable(false); 546 mHandler.postDelayed(mMmTelFeatureUnavailableRunnable, 547 MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS); 548 } else if (reason == ImsStateCallback.REASON_UNKNOWN_PERMANENT_ERROR 549 || reason == ImsStateCallback.REASON_NO_IMS_SERVICE_CONFIGURED) { 550 // Permanently blocked for this subscription. 551 setImsStateAsUnavailable(); 552 notifyImsMmTelFeatureAvailableChanged(); 553 } else if (reason == ImsStateCallback.REASON_IMS_SERVICE_DISCONNECTED) { 554 // Wait for onAvailable for some times and 555 // if it's not available, the IMS state will be set to unavailable. 556 initImsState(); 557 setMmTelFeatureAvailable(false); 558 unregisterImsRegistrationCallback(); 559 unregisterMmTelCapabilityCallback(); 560 mHandler.postDelayed(mMmTelFeatureUnavailableRunnable, 561 MMTEL_FEATURE_AVAILABLE_WAIT_TIME_MILLIS); 562 } else if (reason == ImsStateCallback.REASON_SUBSCRIPTION_INACTIVE) { 563 // The {@link TelephonyDomainSelectionService} will call ImsStateTracker#start 564 // when the subscription changes to register new callbacks. 565 setImsStateAsUnavailable(); 566 unregisterImsRegistrationCallback(); 567 unregisterMmTelCapabilityCallback(); 568 // ImsStateCallback has already been removed after calling onUnavailable. 569 mImsStateCallback = null; 570 notifyImsMmTelFeatureAvailableChanged(); 571 } else { 572 logw("onMmTelFeatureUnavailable: unexpected reason=" + reason); 573 } 574 } 575 576 /** 577 * Called when IMS is registered to the IMS network. 578 */ onImsRegistered(@onNull ImsRegistrationAttributes attributes)579 private void onImsRegistered(@NonNull ImsRegistrationAttributes attributes) { 580 logd("onImsRegistered: " + attributes); 581 582 setImsRegistered(true); 583 setImsAccessNetworkType( 584 imsRegTechToAccessNetworkType(attributes.getRegistrationTechnology())); 585 setImsRegisteredOverCrossSim(attributes.getRegistrationTechnology() 586 == ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM); 587 notifyImsRegistrationStateChanged(); 588 } 589 590 /** 591 * Called when IMS is unregistered from the IMS network. 592 */ onImsUnregistered(@onNull ImsReasonInfo info)593 private void onImsUnregistered(@NonNull ImsReasonInfo info) { 594 logd("onImsUnregistered: " + info); 595 setImsRegistered(false); 596 setImsAccessNetworkType(AccessNetworkType.UNKNOWN); 597 setImsRegisteredOverCrossSim(false); 598 setMmTelCapabilities(new MmTelCapabilities()); 599 notifyImsRegistrationStateChanged(); 600 } 601 602 /** 603 * Called when MMTEL capability is changed - IMS is registered 604 * and the service is currently available over IMS. 605 */ onMmTelCapabilitiesChanged(@onNull MmTelCapabilities capabilities)606 private void onMmTelCapabilitiesChanged(@NonNull MmTelCapabilities capabilities) { 607 logd("onMmTelCapabilitiesChanged: " + capabilities); 608 setMmTelCapabilities(capabilities); 609 notifyImsMmTelCapabilitiesChanged(); 610 } 611 612 /** 613 * Starts listening to monitor the IMS states - 614 * connection state, IMS registration state, and MMTEL capabilities. 615 */ startListeningForImsState()616 private void startListeningForImsState() { 617 if (!SubscriptionManager.isValidSubscriptionId(getSubId())) { 618 setImsStateAsUnavailable(); 619 return; 620 } 621 622 ImsManager imsMngr = mContext.getSystemService(ImsManager.class); 623 mMmTelManager = imsMngr.getImsMmTelManager(getSubId()); 624 initImsState(); 625 registerImsStateCallback(); 626 } 627 628 /** 629 * Stops listening to monitor the IMS states - 630 * connection state, IMS registration state, and MMTEL capabilities. 631 */ stopListeningForImsState()632 private void stopListeningForImsState() { 633 mHandler.removeCallbacks(mMmTelFeatureUnavailableRunnable); 634 635 if (mMmTelManager != null) { 636 unregisterMmTelCapabilityCallback(); 637 unregisterImsRegistrationCallback(); 638 unregisterImsStateCallback(); 639 mMmTelManager = null; 640 } 641 } 642 registerImsStateCallback()643 private void registerImsStateCallback() { 644 if (mImsStateCallback != null) { 645 loge("ImsStateCallback is already registered for sub-" + getSubId()); 646 return; 647 } 648 /** 649 * Listens to the IMS connection state change. 650 */ 651 mImsStateCallback = new ImsStateCallback() { 652 @Override 653 public void onUnavailable(@DisconnectedReason int reason) { 654 onMmTelFeatureUnavailable(reason); 655 } 656 657 @Override 658 public void onAvailable() { 659 onMmTelFeatureAvailable(); 660 } 661 662 @Override 663 public void onError() { 664 // This case will not be happened because this domain selection service 665 // is running on the Telephony service. 666 } 667 }; 668 669 try { 670 mMmTelManager.registerImsStateCallback(mHandler::post, mImsStateCallback); 671 } catch (ImsException e) { 672 loge("Exception when registering ImsStateCallback: " + e); 673 mImsStateCallback = null; 674 } 675 } 676 unregisterImsStateCallback()677 private void unregisterImsStateCallback() { 678 if (mImsStateCallback != null) { 679 try { 680 mMmTelManager.unregisterImsStateCallback(mImsStateCallback); 681 } catch (Exception ignored) { 682 // Ignore the runtime exception while unregistering callback. 683 logd("Exception when unregistering ImsStateCallback: " + ignored); 684 } 685 mImsStateCallback = null; 686 } 687 } 688 registerImsRegistrationCallback()689 private void registerImsRegistrationCallback() { 690 if (mImsRegistrationCallback != null) { 691 logd("RegistrationCallback is already registered for sub-" + getSubId()); 692 return; 693 } 694 /** 695 * Listens to the IMS registration state change. 696 */ 697 mImsRegistrationCallback = new RegistrationManager.RegistrationCallback() { 698 @Override 699 public void onRegistered(@NonNull ImsRegistrationAttributes attributes) { 700 onImsRegistered(attributes); 701 } 702 703 @Override 704 public void onUnregistered(@NonNull ImsReasonInfo info) { 705 onImsUnregistered(info); 706 } 707 }; 708 709 try { 710 mMmTelManager.registerImsRegistrationCallback(mHandler::post, mImsRegistrationCallback); 711 } catch (ImsException e) { 712 loge("Exception when registering RegistrationCallback: " + e); 713 mImsRegistrationCallback = null; 714 } 715 } 716 unregisterImsRegistrationCallback()717 private void unregisterImsRegistrationCallback() { 718 if (mImsRegistrationCallback != null) { 719 try { 720 mMmTelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback); 721 } catch (Exception ignored) { 722 // Ignore the runtime exception while unregistering callback. 723 logd("Exception when unregistering RegistrationCallback: " + ignored); 724 } 725 mImsRegistrationCallback = null; 726 } 727 } 728 registerMmTelCapabilityCallback()729 private void registerMmTelCapabilityCallback() { 730 if (mMmTelCapabilityCallback != null) { 731 logd("CapabilityCallback is already registered for sub-" + getSubId()); 732 return; 733 } 734 /** 735 * Listens to the MmTel feature capabilities change. 736 */ 737 mMmTelCapabilityCallback = new ImsMmTelManager.CapabilityCallback() { 738 @Override 739 public void onCapabilitiesStatusChanged(@NonNull MmTelCapabilities capabilities) { 740 onMmTelCapabilitiesChanged(capabilities); 741 } 742 }; 743 744 try { 745 mMmTelManager.registerMmTelCapabilityCallback(mHandler::post, mMmTelCapabilityCallback); 746 } catch (ImsException e) { 747 loge("Exception when registering CapabilityCallback: " + e); 748 mMmTelCapabilityCallback = null; 749 } 750 } 751 unregisterMmTelCapabilityCallback()752 private void unregisterMmTelCapabilityCallback() { 753 if (mMmTelCapabilityCallback != null) { 754 try { 755 mMmTelManager.unregisterMmTelCapabilityCallback(mMmTelCapabilityCallback); 756 } catch (Exception ignored) { 757 // Ignore the runtime exception while unregistering callback. 758 logd("Exception when unregistering CapabilityCallback: " + ignored); 759 } 760 mMmTelCapabilityCallback = null; 761 } 762 } 763 764 /** Returns a string representation of IMS states. */ imsStateToString()765 public String imsStateToString() { 766 StringBuilder sb = new StringBuilder("{ "); 767 sb.append("MMTEL: featureAvailable=").append(booleanToString(mMmTelFeatureAvailable)); 768 sb.append(", registered=").append(booleanToString(mImsRegistered)); 769 sb.append(", accessNetworkType=").append(accessNetworkTypeToString(mImsAccessNetworkType)); 770 sb.append(", capabilities=").append(mmTelCapabilitiesToString(mMmTelCapabilities)); 771 sb.append(" }"); 772 return sb.toString(); 773 } 774 accessNetworkTypeToString( @adioAccessNetworkType int accessNetworkType)775 protected static String accessNetworkTypeToString( 776 @RadioAccessNetworkType int accessNetworkType) { 777 switch (accessNetworkType) { 778 case AccessNetworkType.UNKNOWN: return "UNKNOWN"; 779 case AccessNetworkType.GERAN: return "GERAN"; 780 case AccessNetworkType.UTRAN: return "UTRAN"; 781 case AccessNetworkType.EUTRAN: return "EUTRAN"; 782 case AccessNetworkType.CDMA2000: return "CDMA2000"; 783 case AccessNetworkType.IWLAN: return "IWLAN"; 784 case AccessNetworkType.NGRAN: return "NGRAN"; 785 default: return Integer.toString(accessNetworkType); 786 } 787 } 788 789 /** Converts the IMS registration technology to the access network type. */ imsRegTechToAccessNetworkType( @msRegistrationImplBase.ImsRegistrationTech int imsRegTech)790 private static @RadioAccessNetworkType int imsRegTechToAccessNetworkType( 791 @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) { 792 switch (imsRegTech) { 793 case ImsRegistrationImplBase.REGISTRATION_TECH_LTE: 794 return AccessNetworkType.EUTRAN; 795 case ImsRegistrationImplBase.REGISTRATION_TECH_NR: 796 return AccessNetworkType.NGRAN; 797 case ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN: 798 case ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM: 799 return AccessNetworkType.IWLAN; 800 default: 801 return AccessNetworkType.UNKNOWN; 802 } 803 } 804 booleanToString(Boolean b)805 private static String booleanToString(Boolean b) { 806 return b == null ? "null" : b.toString(); 807 } 808 mmTelCapabilitiesToString(MmTelCapabilities c)809 private static String mmTelCapabilitiesToString(MmTelCapabilities c) { 810 if (c == null) { 811 return "null"; 812 } 813 StringBuilder sb = new StringBuilder("["); 814 sb.append("voice=").append(c.isCapable(MmTelCapabilities.CAPABILITY_TYPE_VOICE)); 815 sb.append(", video=").append(c.isCapable(MmTelCapabilities.CAPABILITY_TYPE_VIDEO)); 816 sb.append(", ut=").append(c.isCapable(MmTelCapabilities.CAPABILITY_TYPE_UT)); 817 sb.append(", sms=").append(c.isCapable(MmTelCapabilities.CAPABILITY_TYPE_SMS)); 818 sb.append("]"); 819 return sb.toString(); 820 } 821 disconnectedCauseToString(@isconnectedReason int reason)822 private static String disconnectedCauseToString(@DisconnectedReason int reason) { 823 switch (reason) { 824 case ImsStateCallback.REASON_UNKNOWN_TEMPORARY_ERROR: 825 return "UNKNOWN_TEMPORARY_ERROR"; 826 case ImsStateCallback.REASON_UNKNOWN_PERMANENT_ERROR: 827 return "UNKNOWN_PERMANENT_ERROR"; 828 case ImsStateCallback.REASON_IMS_SERVICE_DISCONNECTED: 829 return "IMS_SERVICE_DISCONNECTED"; 830 case ImsStateCallback.REASON_NO_IMS_SERVICE_CONFIGURED: 831 return "NO_IMS_SERVICE_CONFIGURED"; 832 case ImsStateCallback.REASON_SUBSCRIPTION_INACTIVE: 833 return "SUBSCRIPTION_INACTIVE"; 834 case ImsStateCallback.REASON_IMS_SERVICE_NOT_READY: 835 return "IMS_SERVICE_NOT_READY"; 836 default: 837 return Integer.toString(reason); 838 } 839 } 840 841 /** 842 * Dumps this instance into a readable format for dumpsys usage. 843 */ dump(@onNull PrintWriter pw)844 public void dump(@NonNull PrintWriter pw) { 845 IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 846 ipw.println("ImsStateTracker:"); 847 ipw.increaseIndent(); 848 ipw.println("SlotId: " + getSlotId()); 849 ipw.println("SubId: " + getSubId()); 850 ipw.println("ServiceState: " + mServiceState); 851 ipw.println("BarringInfo: " + mBarringInfo); 852 ipw.println("ImsState: " + imsStateToString()); 853 ipw.println("Event Log:"); 854 ipw.increaseIndent(); 855 mEventLog.dump(ipw); 856 ipw.decreaseIndent(); 857 ipw.decreaseIndent(); 858 } 859 logd(String s)860 private void logd(String s) { 861 Log.d(TAG, "[" + getSlotId() + "|" + getSubId() + "] " + s); 862 } 863 logi(String s)864 private void logi(String s) { 865 Log.i(TAG, "[" + getSlotId() + "|" + getSubId() + "] " + s); 866 mEventLog.log("[" + getSlotId() + "|" + getSubId() + "] " + s); 867 } 868 loge(String s)869 private void loge(String s) { 870 Log.e(TAG, "[" + getSlotId() + "|" + getSubId() + "] " + s); 871 mEventLog.log("[" + getSlotId() + "|" + getSubId() + "] " + s); 872 } 873 logw(String s)874 private void logw(String s) { 875 Log.w(TAG, "[" + getSlotId() + "|" + getSubId() + "] " + s); 876 mEventLog.log("[" + getSlotId() + "|" + getSubId() + "] " + s); 877 } 878 } 879