1 /* 2 * Copyright (C) 2017 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.ims; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.Context; 22 import android.os.Handler; 23 import android.os.HandlerExecutor; 24 import android.os.IBinder; 25 import android.os.IInterface; 26 import android.os.Looper; 27 import android.os.Message; 28 import android.os.RemoteCallbackList; 29 import android.os.RemoteException; 30 import android.telephony.Rlog; 31 import android.telephony.SubscriptionInfo; 32 import android.telephony.SubscriptionManager; 33 import android.telephony.TelephonyManager; 34 import android.telephony.ims.ImsCallProfile; 35 import android.telephony.ims.aidl.IImsCapabilityCallback; 36 import android.telephony.ims.aidl.IImsConfig; 37 import android.telephony.ims.aidl.IImsConfigCallback; 38 import android.telephony.ims.aidl.IImsMmTelFeature; 39 import android.telephony.ims.aidl.IImsRegistration; 40 import android.telephony.ims.aidl.IImsRegistrationCallback; 41 import android.telephony.ims.aidl.IImsSmsListener; 42 import android.telephony.ims.feature.CapabilityChangeRequest; 43 import android.telephony.ims.feature.ImsFeature; 44 import android.telephony.ims.feature.MmTelFeature; 45 import android.telephony.ims.stub.ImsRegistrationImplBase; 46 import android.telephony.ims.stub.ImsSmsImplBase; 47 import android.util.ArraySet; 48 import android.util.Log; 49 import android.util.SparseArray; 50 51 import com.android.ims.internal.IImsCallSession; 52 import com.android.ims.internal.IImsEcbm; 53 import com.android.ims.internal.IImsMultiEndpoint; 54 import com.android.ims.internal.IImsServiceFeatureCallback; 55 import com.android.ims.internal.IImsUt; 56 import com.android.internal.annotations.VisibleForTesting; 57 58 import java.util.ArrayList; 59 import java.util.Collections; 60 import java.util.List; 61 import java.util.Set; 62 import java.util.concurrent.Executor; 63 import java.util.stream.Collectors; 64 65 /** 66 * A container of the IImsServiceController binder, which implements all of the ImsFeatures that 67 * the platform currently supports: MMTel and RCS. 68 * @hide 69 */ 70 71 public class MmTelFeatureConnection { 72 protected static final String TAG = "MmTelFeatureConnection"; 73 74 // Manages callbacks to the associated MmTelFeature in mMmTelFeatureConnection. 75 @VisibleForTesting 76 public static abstract class CallbackAdapterManager<T extends IInterface> { 77 private static final String TAG = "CallbackAdapterManager"; 78 79 private final Context mContext; 80 private final Object mLock; 81 // Map of sub id -> List<callbacks> for sub id linked callbacks. 82 private final SparseArray<Set<T>> mCallbackSubscriptionMap = new SparseArray<>(); 83 // List of all active callbacks to ImsService 84 private final RemoteCallbackList<T> mRemoteCallbacks = new RemoteCallbackList<>(); 85 @VisibleForTesting 86 public SubscriptionManager.OnSubscriptionsChangedListener mSubChangedListener; 87 CallbackAdapterManager(Context context, Object lock)88 public CallbackAdapterManager(Context context, Object lock) { 89 mContext = context; 90 mLock = lock; 91 if (Looper.myLooper() == null) { 92 Looper.prepare(); 93 } 94 // Must be created after Looper.prepare() is called, or else we will get an exception. 95 mSubChangedListener = new SubscriptionManager.OnSubscriptionsChangedListener() { 96 @Override 97 public void onSubscriptionsChanged() { 98 SubscriptionManager manager = mContext.getSystemService( 99 SubscriptionManager.class); 100 if (manager == null) { 101 Log.w(TAG, "onSubscriptionsChanged: could not find SubscriptionManager."); 102 return; 103 } 104 List<SubscriptionInfo> subInfos = manager.getActiveSubscriptionInfoList(false); 105 if (subInfos == null) { 106 subInfos = Collections.emptyList(); 107 } 108 Set<Integer> newSubIds = subInfos.stream() 109 .map(SubscriptionInfo::getSubscriptionId) 110 .collect(Collectors.toSet()); 111 synchronized (mLock) { 112 Set<Integer> storedSubIds = new ArraySet<>(mCallbackSubscriptionMap.size()); 113 for (int keyIndex = 0; keyIndex < mCallbackSubscriptionMap.size(); 114 keyIndex++) { 115 storedSubIds.add(mCallbackSubscriptionMap.keyAt(keyIndex)); 116 } 117 // Get the set of sub ids that are in storedSubIds that are not in newSubIds. 118 // This is the set of sub ids that need to be removed. 119 storedSubIds.removeAll(newSubIds); 120 for (Integer subId : storedSubIds) { 121 removeCallbacksForSubscription(subId); 122 } 123 } 124 } 125 }; 126 127 } 128 129 // Add a callback to the MmTelFeature associated with this manager (independent of the) 130 // current subscription. addCallback(T localCallback)131 public final void addCallback(T localCallback) { 132 synchronized (mLock) { 133 // Skip registering to callback subscription map here, because we are registering 134 // for the slot, independent of subscription (deprecated behavior). 135 // Throws a IllegalStateException if this registration fails. 136 registerCallback(localCallback); 137 Log.i(TAG, "Local callback added: " + localCallback); 138 mRemoteCallbacks.register(localCallback); 139 } 140 } 141 142 // Add a callback to be associated with a subscription. If that subscription is removed, 143 // remove the callback and notify the callback that the subscription has been removed. addCallbackForSubscription(T localCallback, int subId)144 public final void addCallbackForSubscription(T localCallback, int subId) { 145 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 146 return; 147 } 148 synchronized (mLock) { 149 addCallback(localCallback); 150 linkCallbackToSubscription(localCallback, subId); 151 } 152 } 153 154 // Removes a callback associated with the MmTelFeature. removeCallback(T localCallback)155 public final void removeCallback(T localCallback) { 156 Log.i(TAG, "Local callback removed: " + localCallback); 157 synchronized (mLock) { 158 if (mRemoteCallbacks.unregister(localCallback)) { 159 // Will only occur if we have record of this callback in mRemoteCallbacks. 160 unregisterCallback(localCallback); 161 } 162 } 163 } 164 165 // Remove an existing callback that has been linked to a subscription. removeCallbackForSubscription(T localCallback, int subId)166 public final void removeCallbackForSubscription(T localCallback, int subId) { 167 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 168 return; 169 } 170 synchronized (mLock) { 171 removeCallback(localCallback); 172 unlinkCallbackFromSubscription(localCallback, subId); 173 } 174 } 175 176 // Links a callback to be tracked by a subscription. If it goes away, emove. linkCallbackToSubscription(T callback, int subId)177 private void linkCallbackToSubscription(T callback, int subId) { 178 synchronized (mLock) { 179 if (mCallbackSubscriptionMap.size() == 0) { 180 // we are about to add the first entry to the map, register for subscriptions 181 //changed listener. 182 registerForSubscriptionsChanged(); 183 } 184 Set<T> callbacksPerSub = mCallbackSubscriptionMap.get(subId); 185 if (callbacksPerSub == null) { 186 // the callback list has not been created yet for this subscription. 187 callbacksPerSub = new ArraySet<>(); 188 mCallbackSubscriptionMap.put(subId, callbacksPerSub); 189 } 190 callbacksPerSub.add(callback); 191 } 192 } 193 194 // Unlink the callback from the associated subscription. unlinkCallbackFromSubscription(T callback, int subId)195 private void unlinkCallbackFromSubscription(T callback, int subId) { 196 synchronized (mLock) { 197 Set<T> callbacksPerSub = mCallbackSubscriptionMap.get(subId); 198 if (callbacksPerSub != null) { 199 callbacksPerSub.remove(callback); 200 if (callbacksPerSub.isEmpty()) { 201 mCallbackSubscriptionMap.remove(subId); 202 } 203 } 204 if (mCallbackSubscriptionMap.size() == 0) { 205 unregisterForSubscriptionsChanged(); 206 } 207 } 208 } 209 210 // Removes all of the callbacks that have been registered to the subscription specified. 211 // This happens when Telephony sends an indication that the subscriptions have changed. removeCallbacksForSubscription(int subId)212 private void removeCallbacksForSubscription(int subId) { 213 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 214 return; 215 } 216 synchronized (mLock) { 217 Set<T> callbacksPerSub = mCallbackSubscriptionMap.get(subId); 218 if (callbacksPerSub == null) { 219 // no callbacks registered for this subscription. 220 return; 221 } 222 // clear all registered callbacks in the subscription map for this subscription. 223 mCallbackSubscriptionMap.remove(subId); 224 for (T callback : callbacksPerSub) { 225 removeCallback(callback); 226 } 227 // If there are no more callbacks being tracked, remove subscriptions changed 228 // listener. 229 if (mCallbackSubscriptionMap.size() == 0) { 230 unregisterForSubscriptionsChanged(); 231 } 232 } 233 } 234 235 // Clear the Subscription -> Callback map because the ImsService connection is no longer 236 // current. clearCallbacksForAllSubscriptions()237 private void clearCallbacksForAllSubscriptions() { 238 synchronized (mLock) { 239 List<Integer> keys = new ArrayList<>(); 240 for (int keyIndex = 0; keyIndex < mCallbackSubscriptionMap.size(); keyIndex++) { 241 keys.add(mCallbackSubscriptionMap.keyAt(keyIndex)); 242 } 243 keys.forEach(this::removeCallbacksForSubscription); 244 } 245 } 246 registerForSubscriptionsChanged()247 private void registerForSubscriptionsChanged() { 248 SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class); 249 if (manager != null) { 250 manager.addOnSubscriptionsChangedListener(mSubChangedListener); 251 } else { 252 Log.w(TAG, "registerForSubscriptionsChanged: could not find SubscriptionManager."); 253 } 254 } 255 unregisterForSubscriptionsChanged()256 private void unregisterForSubscriptionsChanged() { 257 SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class); 258 if (manager != null) { 259 manager.removeOnSubscriptionsChangedListener(mSubChangedListener); 260 } else { 261 Log.w(TAG, "unregisterForSubscriptionsChanged: could not find" 262 + " SubscriptionManager."); 263 } 264 } 265 266 // The ImsService these callbacks are registered to has become unavailable or crashed, or 267 // the ImsResolver has switched to a new ImsService. In these cases, clean up all existing 268 // callbacks. close()269 public final void close() { 270 synchronized (mLock) { 271 final int lastCallbackIndex = mRemoteCallbacks.getRegisteredCallbackCount() - 1; 272 for(int ii = lastCallbackIndex; ii >= 0; ii --) { 273 T callbackItem = mRemoteCallbacks.getRegisteredCallbackItem(ii); 274 unregisterCallback(callbackItem); 275 mRemoteCallbacks.unregister(callbackItem); 276 } 277 clearCallbacksForAllSubscriptions(); 278 Log.i(TAG, "Closing connection and clearing callbacks"); 279 } 280 } 281 282 // A callback has been registered. Register that callback with the MmTelFeature. registerCallback(T localCallback)283 public abstract void registerCallback(T localCallback); 284 285 // A callback has been removed, unregister that callback with the MmTelFeature. unregisterCallback(T localCallback)286 public abstract void unregisterCallback(T localCallback); 287 } 288 289 private class ImsRegistrationCallbackAdapter extends 290 CallbackAdapterManager<IImsRegistrationCallback> { 291 ImsRegistrationCallbackAdapter(Context context, Object lock)292 public ImsRegistrationCallbackAdapter(Context context, Object lock) { 293 super(context, lock); 294 } 295 296 @Override registerCallback(IImsRegistrationCallback localCallback)297 public void registerCallback(IImsRegistrationCallback localCallback) { 298 IImsRegistration imsRegistration = getRegistration(); 299 if (imsRegistration != null) { 300 try { 301 imsRegistration.addRegistrationCallback(localCallback); 302 } catch (RemoteException e) { 303 throw new IllegalStateException("ImsRegistrationCallbackAdapter: MmTelFeature" 304 + " binder is dead."); 305 } 306 } else { 307 Log.e(TAG, "ImsRegistrationCallbackAdapter: ImsRegistration is null"); 308 throw new IllegalStateException("ImsRegistrationCallbackAdapter: MmTelFeature is" 309 + "not available!"); 310 } 311 } 312 313 @Override unregisterCallback(IImsRegistrationCallback localCallback)314 public void unregisterCallback(IImsRegistrationCallback localCallback) { 315 IImsRegistration imsRegistration = getRegistration(); 316 if (imsRegistration != null) { 317 try { 318 imsRegistration.removeRegistrationCallback(localCallback); 319 } catch (RemoteException e) { 320 Log.w(TAG, "ImsRegistrationCallbackAdapter - unregisterCallback: couldn't" 321 + " remove registration callback"); 322 } 323 } else { 324 Log.e(TAG, "ImsRegistrationCallbackAdapter: ImsRegistration is null"); 325 } 326 } 327 } 328 329 private class CapabilityCallbackManager extends CallbackAdapterManager<IImsCapabilityCallback> { 330 CapabilityCallbackManager(Context context, Object lock)331 public CapabilityCallbackManager(Context context, Object lock) { 332 super(context, lock); 333 } 334 335 @Override registerCallback(IImsCapabilityCallback localCallback)336 public void registerCallback(IImsCapabilityCallback localCallback) { 337 IImsMmTelFeature binder; 338 synchronized (mLock) { 339 try { 340 checkServiceIsReady(); 341 binder = getServiceInterface(mBinder); 342 } catch (RemoteException e) { 343 throw new IllegalStateException("CapabilityCallbackManager - MmTelFeature" 344 + " binder is dead."); 345 } 346 } 347 if (binder != null) { 348 try { 349 binder.addCapabilityCallback(localCallback); 350 } catch (RemoteException e) { 351 throw new IllegalStateException(" CapabilityCallbackManager - MmTelFeature" 352 + " binder is null."); 353 } 354 } else { 355 Log.w(TAG, "CapabilityCallbackManager, register: Couldn't get binder"); 356 throw new IllegalStateException("CapabilityCallbackManager: MmTelFeature is" 357 + " not available!"); 358 } 359 } 360 361 @Override unregisterCallback(IImsCapabilityCallback localCallback)362 public void unregisterCallback(IImsCapabilityCallback localCallback) { 363 IImsMmTelFeature binder; 364 synchronized (mLock) { 365 try { 366 checkServiceIsReady(); 367 binder = getServiceInterface(mBinder); 368 } catch (RemoteException e) { 369 // binder is null 370 Log.w(TAG, "CapabilityCallbackManager, unregister: couldn't get binder."); 371 return; 372 } 373 } 374 if (binder != null) { 375 try { 376 binder.removeCapabilityCallback(localCallback); 377 } catch (RemoteException e) { 378 Log.w(TAG, "CapabilityCallbackManager, unregister: Binder is dead."); 379 } 380 } else { 381 Log.w(TAG, "CapabilityCallbackManager, unregister: binder is null."); 382 } 383 } 384 } 385 386 private class ProvisioningCallbackManager extends CallbackAdapterManager<IImsConfigCallback> { ProvisioningCallbackManager(Context context, Object lock)387 public ProvisioningCallbackManager (Context context, Object lock) { 388 super(context, lock); 389 } 390 391 @Override registerCallback(IImsConfigCallback localCallback)392 public void registerCallback(IImsConfigCallback localCallback) { 393 IImsConfig binder = getConfigInterface(); 394 if (binder == null) { 395 // Config interface is not currently available. 396 Log.w(TAG, "ProvisioningCallbackManager - couldn't register, binder is null."); 397 throw new IllegalStateException("ImsConfig is not available!"); 398 } 399 try { 400 binder.addImsConfigCallback(localCallback); 401 }catch (RemoteException e) { 402 throw new IllegalStateException("ImsService is not available!"); 403 } 404 } 405 406 @Override unregisterCallback(IImsConfigCallback localCallback)407 public void unregisterCallback(IImsConfigCallback localCallback) { 408 IImsConfig binder = getConfigInterface(); 409 if (binder == null) { 410 Log.w(TAG, "ProvisioningCallbackManager - couldn't unregister, binder is null."); 411 return; 412 } 413 try { 414 binder.removeImsConfigCallback(localCallback); 415 } catch (RemoteException e) { 416 Log.w(TAG, "ProvisioningCallbackManager - couldn't unregister, binder is dead."); 417 } 418 } 419 } 420 421 protected final int mSlotId; 422 protected IBinder mBinder; 423 private Context mContext; 424 private Executor mExecutor; 425 426 private volatile boolean mIsAvailable = false; 427 // ImsFeature Status from the ImsService. Cached. 428 private Integer mFeatureStateCached = null; 429 private IFeatureUpdate mStatusCallback; 430 private final Object mLock = new Object(); 431 // Updated by IImsServiceFeatureCallback when FEATURE_EMERGENCY_MMTEL is sent. 432 private boolean mSupportsEmergencyCalling = false; 433 private static boolean sImsSupportedOnDevice = true; 434 435 // Cache the Registration and Config interfaces as long as the MmTel feature is connected. If 436 // it becomes disconnected, invalidate. 437 private IImsRegistration mRegistrationBinder; 438 private IImsConfig mConfigBinder; 439 440 private final IBinder.DeathRecipient mDeathRecipient = () -> { 441 Log.w(TAG, "DeathRecipient triggered, binder died."); 442 if (mContext != null && Looper.getMainLooper() != null) { 443 // Move this signal to the main thread, notifying ImsManager of the Binder 444 // death on another thread may lead to deadlocks. 445 mContext.getMainExecutor().execute(this::onRemovedOrDied); 446 return; 447 } 448 // No choice - execute on the current Binder thread. 449 onRemovedOrDied(); 450 }; 451 452 private final ImsRegistrationCallbackAdapter mRegistrationCallbackManager; 453 private final CapabilityCallbackManager mCapabilityCallbackManager; 454 private final ProvisioningCallbackManager mProvisioningCallbackManager; 455 create(Context context , int slotId)456 public static @NonNull MmTelFeatureConnection create(Context context , int slotId) { 457 MmTelFeatureConnection serviceProxy = new MmTelFeatureConnection(context, slotId); 458 if (!ImsManager.isImsSupportedOnDevice(context)) { 459 // Return empty service proxy in the case that IMS is not supported. 460 sImsSupportedOnDevice = false; 461 return serviceProxy; 462 } 463 464 TelephonyManager tm = getTelephonyManager(context); 465 if (tm == null) { 466 Rlog.w(TAG, "create: TelephonyManager is null!"); 467 // Binder can be unset in this case because it will be torn down/recreated as part of 468 // a retry mechanism until the serviceProxy binder is set successfully. 469 return serviceProxy; 470 } 471 472 IImsMmTelFeature binder = tm.getImsMmTelFeatureAndListen(slotId, 473 serviceProxy.getListener()); 474 if (binder != null) { 475 serviceProxy.setBinder(binder.asBinder()); 476 // Trigger the cache to be updated for feature status. 477 serviceProxy.getFeatureState(); 478 } else { 479 Rlog.w(TAG, "create: binder is null! Slot Id: " + slotId); 480 } 481 return serviceProxy; 482 } 483 getTelephonyManager(Context context)484 public static TelephonyManager getTelephonyManager(Context context) { 485 return (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 486 } 487 488 public interface IFeatureUpdate { 489 /** 490 * Called when the ImsFeature has changed its state. Use 491 * {@link ImsFeature#getFeatureState()} to get the new state. 492 */ notifyStateChanged()493 void notifyStateChanged(); 494 495 /** 496 * Called when the ImsFeature has become unavailable due to the binder switching or app 497 * crashing. A new ImsServiceProxy should be requested for that feature. 498 */ notifyUnavailable()499 void notifyUnavailable(); 500 } 501 502 private final IImsServiceFeatureCallback mListenerBinder = 503 new IImsServiceFeatureCallback.Stub() { 504 505 @Override 506 public void imsFeatureCreated(int slotId, int feature) { 507 mExecutor.execute(() -> { 508 // The feature has been enabled. This happens when the feature is first created and 509 // may happen when the feature is re-enabled. 510 synchronized (mLock) { 511 if(mSlotId != slotId) { 512 return; 513 } 514 switch (feature) { 515 case ImsFeature.FEATURE_MMTEL: { 516 if (!mIsAvailable) { 517 Log.i(TAG, "MmTel enabled on slotId: " + slotId); 518 mIsAvailable = true; 519 } 520 break; 521 } 522 case ImsFeature.FEATURE_EMERGENCY_MMTEL: { 523 mSupportsEmergencyCalling = true; 524 Log.i(TAG, "Emergency calling enabled on slotId: " + slotId); 525 break; 526 } 527 } 528 } 529 }); 530 } 531 532 @Override 533 public void imsFeatureRemoved(int slotId, int feature) { 534 mExecutor.execute(() -> { 535 synchronized (mLock) { 536 if (mSlotId != slotId) { 537 return; 538 } 539 switch (feature) { 540 case ImsFeature.FEATURE_MMTEL: { 541 Log.i(TAG, "MmTel removed on slotId: " + slotId); 542 onRemovedOrDied(); 543 break; 544 } 545 case ImsFeature.FEATURE_EMERGENCY_MMTEL: { 546 mSupportsEmergencyCalling = false; 547 Log.i(TAG, "Emergency calling disabled on slotId: " + slotId); 548 break; 549 } 550 } 551 } 552 }); 553 } 554 555 @Override 556 public void imsStatusChanged(int slotId, int feature, int status) { 557 mExecutor.execute(() -> { 558 synchronized (mLock) { 559 Log.i(TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature + 560 " status: " + status); 561 if (mSlotId == slotId && feature == ImsFeature.FEATURE_MMTEL) { 562 mFeatureStateCached = status; 563 if (mStatusCallback != null) { 564 mStatusCallback.notifyStateChanged(); 565 } 566 } 567 } 568 }); 569 } 570 }; 571 MmTelFeatureConnection(Context context, int slotId)572 public MmTelFeatureConnection(Context context, int slotId) { 573 mSlotId = slotId; 574 mContext = context; 575 // Callbacks should be scheduled on the main thread. 576 if (context.getMainLooper() != null) { 577 mExecutor = context.getMainExecutor(); 578 } else { 579 // Fallback to the current thread. 580 if (Looper.myLooper() == null) { 581 Looper.prepare(); 582 } 583 mExecutor = new HandlerExecutor(new Handler(Looper.myLooper())); 584 } 585 mRegistrationCallbackManager = new ImsRegistrationCallbackAdapter(context, mLock); 586 mCapabilityCallbackManager = new CapabilityCallbackManager(context, mLock); 587 mProvisioningCallbackManager = new ProvisioningCallbackManager(context, mLock); 588 } 589 590 /** 591 * Called when the MmTelFeature has either been removed by Telephony or crashed. 592 */ onRemovedOrDied()593 private void onRemovedOrDied() { 594 synchronized (mLock) { 595 mRegistrationCallbackManager.close(); 596 mCapabilityCallbackManager.close(); 597 mProvisioningCallbackManager.close(); 598 if (mIsAvailable) { 599 mIsAvailable = false; 600 // invalidate caches. 601 mRegistrationBinder = null; 602 mConfigBinder = null; 603 if (mBinder != null) { 604 mBinder.unlinkToDeath(mDeathRecipient, 0); 605 } 606 if (mStatusCallback != null) { 607 mStatusCallback.notifyUnavailable(); 608 } 609 } 610 } 611 } 612 getRegistration()613 private @Nullable IImsRegistration getRegistration() { 614 synchronized (mLock) { 615 // null if cache is invalid; 616 if (mRegistrationBinder != null) { 617 return mRegistrationBinder; 618 } 619 } 620 TelephonyManager tm = getTelephonyManager(mContext); 621 // We don't want to synchronize on a binder call to another process. 622 IImsRegistration regBinder = tm != null 623 ? tm.getImsRegistration(mSlotId, ImsFeature.FEATURE_MMTEL) : null; 624 synchronized (mLock) { 625 // mRegistrationBinder may have changed while we tried to get the registration 626 // interface. 627 if (mRegistrationBinder == null) { 628 mRegistrationBinder = regBinder; 629 } 630 } 631 return mRegistrationBinder; 632 } 633 getConfig()634 private IImsConfig getConfig() { 635 synchronized (mLock) { 636 // null if cache is invalid; 637 if (mConfigBinder != null) { 638 return mConfigBinder; 639 } 640 } 641 TelephonyManager tm = getTelephonyManager(mContext); 642 IImsConfig configBinder = tm != null 643 ? tm.getImsConfig(mSlotId, ImsFeature.FEATURE_MMTEL) : null; 644 synchronized (mLock) { 645 // mConfigBinder may have changed while we tried to get the config interface. 646 if (mConfigBinder == null) { 647 mConfigBinder = configBinder; 648 } 649 } 650 return mConfigBinder; 651 } 652 isEmergencyMmTelAvailable()653 public boolean isEmergencyMmTelAvailable() { 654 return mSupportsEmergencyCalling; 655 } 656 getListener()657 public IImsServiceFeatureCallback getListener() { 658 return mListenerBinder; 659 } 660 setBinder(IBinder binder)661 public void setBinder(IBinder binder) { 662 synchronized (mLock) { 663 mBinder = binder; 664 try { 665 if (mBinder != null) { 666 mBinder.linkToDeath(mDeathRecipient, 0); 667 } 668 } catch (RemoteException e) { 669 // No need to do anything if the binder is already dead. 670 } 671 } 672 } 673 674 /** 675 * Opens the connection to the {@link MmTelFeature} and establishes a listener back to the 676 * framework. Calling this method multiple times will reset the listener attached to the 677 * {@link MmTelFeature}. 678 * @param listener A {@link MmTelFeature.Listener} that will be used by the {@link MmTelFeature} 679 * to notify the framework of updates. 680 */ openConnection(MmTelFeature.Listener listener)681 public void openConnection(MmTelFeature.Listener listener) throws RemoteException { 682 synchronized (mLock) { 683 checkServiceIsReady(); 684 getServiceInterface(mBinder).setListener(listener); 685 } 686 } 687 closeConnection()688 public void closeConnection() { 689 mRegistrationCallbackManager.close(); 690 mCapabilityCallbackManager.close(); 691 mProvisioningCallbackManager.close(); 692 try { 693 synchronized (mLock) { 694 if (isBinderAlive()) { 695 getServiceInterface(mBinder).setListener(null); 696 } 697 } 698 } catch (RemoteException e) { 699 Log.w(TAG, "closeConnection: couldn't remove listener!"); 700 } 701 } 702 addRegistrationCallback(IImsRegistrationCallback callback)703 public void addRegistrationCallback(IImsRegistrationCallback callback) { 704 mRegistrationCallbackManager.addCallback(callback); 705 } 706 addRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)707 public void addRegistrationCallbackForSubscription(IImsRegistrationCallback callback, 708 int subId) { 709 mRegistrationCallbackManager.addCallbackForSubscription(callback , subId); 710 } 711 removeRegistrationCallback(IImsRegistrationCallback callback)712 public void removeRegistrationCallback(IImsRegistrationCallback callback) { 713 mRegistrationCallbackManager.removeCallback(callback); 714 } 715 removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback, int subId)716 public void removeRegistrationCallbackForSubscription(IImsRegistrationCallback callback, 717 int subId) { 718 mRegistrationCallbackManager.removeCallbackForSubscription(callback, subId); 719 } 720 addCapabilityCallback(IImsCapabilityCallback callback)721 public void addCapabilityCallback(IImsCapabilityCallback callback) { 722 mCapabilityCallbackManager.addCallback(callback); 723 } 724 addCapabilityCallbackForSubscription(IImsCapabilityCallback callback, int subId)725 public void addCapabilityCallbackForSubscription(IImsCapabilityCallback callback, 726 int subId) { 727 mCapabilityCallbackManager.addCallbackForSubscription(callback, subId); 728 } 729 removeCapabilityCallback(IImsCapabilityCallback callback)730 public void removeCapabilityCallback(IImsCapabilityCallback callback) { 731 mCapabilityCallbackManager.removeCallback(callback); 732 } 733 removeCapabilityCallbackForSubscription(IImsCapabilityCallback callback, int subId)734 public void removeCapabilityCallbackForSubscription(IImsCapabilityCallback callback, 735 int subId) { 736 mCapabilityCallbackManager.removeCallbackForSubscription(callback , subId); 737 } 738 addProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId)739 public void addProvisioningCallbackForSubscription(IImsConfigCallback callback, 740 int subId) { 741 mProvisioningCallbackManager.addCallbackForSubscription(callback, subId); 742 } 743 removeProvisioningCallbackForSubscription(IImsConfigCallback callback, int subId)744 public void removeProvisioningCallbackForSubscription(IImsConfigCallback callback, 745 int subId) { 746 mProvisioningCallbackManager.removeCallbackForSubscription(callback , subId); 747 } 748 changeEnabledCapabilities(CapabilityChangeRequest request, IImsCapabilityCallback callback)749 public void changeEnabledCapabilities(CapabilityChangeRequest request, 750 IImsCapabilityCallback callback) throws RemoteException { 751 synchronized (mLock) { 752 checkServiceIsReady(); 753 getServiceInterface(mBinder).changeCapabilitiesConfiguration(request, callback); 754 } 755 } 756 queryEnabledCapabilities(int capability, int radioTech, IImsCapabilityCallback callback)757 public void queryEnabledCapabilities(int capability, int radioTech, 758 IImsCapabilityCallback callback) throws RemoteException { 759 synchronized (mLock) { 760 checkServiceIsReady(); 761 getServiceInterface(mBinder).queryCapabilityConfiguration(capability, radioTech, 762 callback); 763 } 764 } 765 queryCapabilityStatus()766 public MmTelFeature.MmTelCapabilities queryCapabilityStatus() throws RemoteException { 767 synchronized (mLock) { 768 checkServiceIsReady(); 769 return new MmTelFeature.MmTelCapabilities( 770 getServiceInterface(mBinder).queryCapabilityStatus()); 771 } 772 } 773 createCallProfile(int callServiceType, int callType)774 public ImsCallProfile createCallProfile(int callServiceType, int callType) 775 throws RemoteException { 776 synchronized (mLock) { 777 checkServiceIsReady(); 778 return getServiceInterface(mBinder).createCallProfile(callServiceType, callType); 779 } 780 } 781 createCallSession(ImsCallProfile profile)782 public IImsCallSession createCallSession(ImsCallProfile profile) 783 throws RemoteException { 784 synchronized (mLock) { 785 checkServiceIsReady(); 786 return getServiceInterface(mBinder).createCallSession(profile); 787 } 788 } 789 getUtInterface()790 public IImsUt getUtInterface() throws RemoteException { 791 synchronized (mLock) { 792 checkServiceIsReady(); 793 return getServiceInterface(mBinder).getUtInterface(); 794 } 795 } 796 getConfigInterface()797 public IImsConfig getConfigInterface() { 798 return getConfig(); 799 } 800 getRegistrationTech()801 public @ImsRegistrationImplBase.ImsRegistrationTech int getRegistrationTech() 802 throws RemoteException { 803 IImsRegistration registration = getRegistration(); 804 if (registration != null) { 805 return registration.getRegistrationTechnology(); 806 } else { 807 return ImsRegistrationImplBase.REGISTRATION_TECH_NONE; 808 } 809 } 810 getEcbmInterface()811 public IImsEcbm getEcbmInterface() throws RemoteException { 812 synchronized (mLock) { 813 checkServiceIsReady(); 814 return getServiceInterface(mBinder).getEcbmInterface(); 815 } 816 } 817 setUiTTYMode(int uiTtyMode, Message onComplete)818 public void setUiTTYMode(int uiTtyMode, Message onComplete) 819 throws RemoteException { 820 synchronized (mLock) { 821 checkServiceIsReady(); 822 getServiceInterface(mBinder).setUiTtyMode(uiTtyMode, onComplete); 823 } 824 } 825 getMultiEndpointInterface()826 public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException { 827 synchronized (mLock) { 828 checkServiceIsReady(); 829 return getServiceInterface(mBinder).getMultiEndpointInterface(); 830 } 831 } 832 sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, byte[] pdu)833 public void sendSms(int token, int messageRef, String format, String smsc, boolean isRetry, 834 byte[] pdu) throws RemoteException { 835 synchronized (mLock) { 836 checkServiceIsReady(); 837 getServiceInterface(mBinder).sendSms(token, messageRef, format, smsc, isRetry, 838 pdu); 839 } 840 } 841 acknowledgeSms(int token, int messageRef, @ImsSmsImplBase.SendStatusResult int result)842 public void acknowledgeSms(int token, int messageRef, 843 @ImsSmsImplBase.SendStatusResult int result) throws RemoteException { 844 synchronized (mLock) { 845 checkServiceIsReady(); 846 getServiceInterface(mBinder).acknowledgeSms(token, messageRef, result); 847 } 848 } 849 acknowledgeSmsReport(int token, int messageRef, @ImsSmsImplBase.StatusReportResult int result)850 public void acknowledgeSmsReport(int token, int messageRef, 851 @ImsSmsImplBase.StatusReportResult int result) throws RemoteException { 852 synchronized (mLock) { 853 checkServiceIsReady(); 854 getServiceInterface(mBinder).acknowledgeSmsReport(token, messageRef, result); 855 } 856 } 857 getSmsFormat()858 public String getSmsFormat() throws RemoteException { 859 synchronized (mLock) { 860 checkServiceIsReady(); 861 return getServiceInterface(mBinder).getSmsFormat(); 862 } 863 } 864 onSmsReady()865 public void onSmsReady() throws RemoteException { 866 synchronized (mLock) { 867 checkServiceIsReady(); 868 getServiceInterface(mBinder).onSmsReady(); 869 } 870 } 871 setSmsListener(IImsSmsListener listener)872 public void setSmsListener(IImsSmsListener listener) throws RemoteException { 873 synchronized (mLock) { 874 checkServiceIsReady(); 875 getServiceInterface(mBinder).setSmsListener(listener); 876 } 877 } 878 shouldProcessCall(boolean isEmergency, String[] numbers)879 public @MmTelFeature.ProcessCallResult int shouldProcessCall(boolean isEmergency, 880 String[] numbers) throws RemoteException { 881 if (isEmergency && !isEmergencyMmTelAvailable()) { 882 // Don't query the ImsService if emergency calling is not available on the ImsService. 883 Log.i(TAG, "MmTel does not support emergency over IMS, fallback to CS."); 884 return MmTelFeature.PROCESS_CALL_CSFB; 885 } 886 synchronized (mLock) { 887 checkServiceIsReady(); 888 return getServiceInterface(mBinder).shouldProcessCall(numbers); 889 } 890 } 891 892 /** 893 * @return an integer describing the current Feature Status, defined in 894 * {@link ImsFeature.ImsState}. 895 */ getFeatureState()896 public int getFeatureState() { 897 synchronized (mLock) { 898 if (isBinderAlive() && mFeatureStateCached != null) { 899 return mFeatureStateCached; 900 } 901 } 902 // Don't synchronize on Binder call. 903 Integer status = retrieveFeatureState(); 904 synchronized (mLock) { 905 if (status == null) { 906 return ImsFeature.STATE_UNAVAILABLE; 907 } 908 // Cache only non-null value for feature status. 909 mFeatureStateCached = status; 910 } 911 Log.i(TAG, "getFeatureState - returning " + status); 912 return status; 913 } 914 915 /** 916 * Internal method used to retrieve the feature status from the corresponding ImsService. 917 */ retrieveFeatureState()918 private Integer retrieveFeatureState() { 919 if (mBinder != null) { 920 try { 921 return getServiceInterface(mBinder).getFeatureState(); 922 } catch (RemoteException e) { 923 // Status check failed, don't update cache 924 } 925 } 926 return null; 927 } 928 929 /** 930 * @param c Callback that will fire when the feature status has changed. 931 */ setStatusCallback(IFeatureUpdate c)932 public void setStatusCallback(IFeatureUpdate c) { 933 mStatusCallback = c; 934 } 935 936 /** 937 * @return Returns true if the ImsService is ready to take commands, false otherwise. If this 938 * method returns false, it doesn't mean that the Binder connection is not available (use 939 * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands 940 * at this time. 941 * 942 * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take 943 * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_UNAVAILABLE}. 944 */ isBinderReady()945 public boolean isBinderReady() { 946 return isBinderAlive() && getFeatureState() == ImsFeature.STATE_READY; 947 } 948 949 /** 950 * @return false if the binder connection is no longer alive. 951 */ isBinderAlive()952 public boolean isBinderAlive() { 953 return mIsAvailable && mBinder != null && mBinder.isBinderAlive(); 954 } 955 checkServiceIsReady()956 private void checkServiceIsReady() throws RemoteException { 957 if (!sImsSupportedOnDevice) { 958 throw new RemoteException("IMS is not supported on this device."); 959 } 960 if (!isBinderReady()) { 961 throw new RemoteException("ImsServiceProxy is not ready to accept commands."); 962 } 963 } 964 getServiceInterface(IBinder b)965 private IImsMmTelFeature getServiceInterface(IBinder b) { 966 return IImsMmTelFeature.Stub.asInterface(b); 967 } 968 } 969