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.internal.telephony.ims; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.content.pm.IPackageManager; 24 import android.os.Handler; 25 import android.os.HandlerThread; 26 import android.os.IBinder; 27 import android.os.IInterface; 28 import android.os.RemoteException; 29 import android.os.ServiceManager; 30 import android.telephony.ims.ImsService; 31 import android.telephony.ims.aidl.IImsConfig; 32 import android.telephony.ims.aidl.IImsMmTelFeature; 33 import android.telephony.ims.aidl.IImsRcsFeature; 34 import android.telephony.ims.aidl.IImsRegistration; 35 import android.telephony.ims.aidl.IImsServiceController; 36 import android.telephony.ims.feature.ImsFeature; 37 import android.telephony.ims.stub.ImsFeatureConfiguration; 38 import android.util.Log; 39 40 import com.android.ims.internal.IImsFeatureStatusCallback; 41 import com.android.ims.internal.IImsServiceFeatureCallback; 42 import com.android.internal.annotations.VisibleForTesting; 43 import com.android.internal.telephony.ExponentialBackoff; 44 45 import java.util.HashSet; 46 import java.util.Iterator; 47 import java.util.Set; 48 import java.util.concurrent.ConcurrentHashMap; 49 50 /** 51 * Manages the Binding lifecycle of one ImsService as well as the relevant ImsFeatures that the 52 * ImsService will support. 53 * 54 * When the ImsService is first bound, {@link ImsService#createMmTelFeature(int)} and 55 * {@link ImsService#createRcsFeature(int)} will be called 56 * on each feature that the service supports. For each ImsFeature that is created, 57 * {@link ImsServiceControllerCallbacks#imsServiceFeatureCreated} will be called to notify the 58 * listener that the ImsService now supports that feature. 59 * 60 * When {@link #changeImsServiceFeatures} is called with a set of features that is different from 61 * the original set, create*Feature and {@link IImsServiceController#removeImsFeature} will be 62 * called for each feature that is created/removed. 63 */ 64 public class ImsServiceController { 65 66 class ImsServiceConnection implements ServiceConnection { 67 68 @Override onServiceConnected(ComponentName name, IBinder service)69 public void onServiceConnected(ComponentName name, IBinder service) { 70 mBackoff.stop(); 71 synchronized (mLock) { 72 mIsBound = true; 73 mIsBinding = false; 74 Log.d(LOG_TAG, "ImsService(" + name + "): onServiceConnected with binder: " 75 + service); 76 if (service != null) { 77 try { 78 setServiceController(service); 79 notifyImsServiceReady(); 80 // create all associated features in the ImsService 81 for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) { 82 addImsServiceFeature(i); 83 } 84 } catch (RemoteException e) { 85 mIsBound = false; 86 mIsBinding = false; 87 // Remote exception means that the binder already died. 88 cleanupConnection(); 89 startDelayedRebindToService(); 90 Log.e(LOG_TAG, "ImsService(" + name + ") RemoteException:" 91 + e.getMessage()); 92 } 93 } 94 } 95 } 96 97 @Override onServiceDisconnected(ComponentName name)98 public void onServiceDisconnected(ComponentName name) { 99 synchronized (mLock) { 100 mIsBinding = false; 101 } 102 cleanupConnection(); 103 Log.w(LOG_TAG, "ImsService(" + name + "): onServiceDisconnected. Waiting..."); 104 // Service disconnected, but we are still technically bound. Waiting for reconnect. 105 } 106 107 @Override onBindingDied(ComponentName name)108 public void onBindingDied(ComponentName name) { 109 synchronized (mLock) { 110 mIsBinding = false; 111 mIsBound = false; 112 } 113 cleanupConnection(); 114 Log.w(LOG_TAG, "ImsService(" + name + "): onBindingDied. Starting rebind..."); 115 startDelayedRebindToService(); 116 } 117 cleanupConnection()118 private void cleanupConnection() { 119 cleanupAllFeatures(); 120 cleanUpService(); 121 } 122 } 123 124 private ImsService.Listener mFeatureChangedListener = new ImsService.Listener() { 125 @Override 126 public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) { 127 if (mCallbacks == null) { 128 return; 129 } 130 mCallbacks.imsServiceFeaturesChanged(c, ImsServiceController.this); 131 } 132 }; 133 134 /** 135 * Defines callbacks that are used by the ImsServiceController to notify when an ImsService 136 * has created or removed a new feature as well as the associated ImsServiceController. 137 */ 138 public interface ImsServiceControllerCallbacks { 139 /** 140 * Called by ImsServiceController when a new MMTEL or RCS feature has been created. 141 */ imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller)142 void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller); 143 /** 144 * Called by ImsServiceController when a new MMTEL or RCS feature has been removed. 145 */ imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller)146 void imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller); 147 148 /** 149 * Called by the ImsServiceController when the ImsService has notified the framework that 150 * its features have changed. 151 */ imsServiceFeaturesChanged(ImsFeatureConfiguration config, ImsServiceController controller)152 void imsServiceFeaturesChanged(ImsFeatureConfiguration config, 153 ImsServiceController controller); 154 } 155 156 /** 157 * Returns the currently defined rebind retry timeout. Used for testing. 158 */ 159 @VisibleForTesting 160 public interface RebindRetry { 161 /** 162 * Returns a long in ms indicating how long the ImsServiceController should wait before 163 * rebinding for the first time. 164 */ getStartDelay()165 long getStartDelay(); 166 167 /** 168 * Returns a long in ms indicating the maximum time the ImsServiceController should wait 169 * before rebinding. 170 */ getMaximumDelay()171 long getMaximumDelay(); 172 } 173 174 private static final String LOG_TAG = "ImsServiceController"; 175 private static final int REBIND_START_DELAY_MS = 2 * 1000; // 2 seconds 176 private static final int REBIND_MAXIMUM_DELAY_MS = 60 * 1000; // 1 minute 177 private final ComponentName mComponentName; 178 private final HandlerThread mHandlerThread = new HandlerThread("ImsServiceControllerHandler"); 179 private final IPackageManager mPackageManager; 180 private ImsServiceControllerCallbacks mCallbacks; 181 private ExponentialBackoff mBackoff; 182 183 private boolean mIsBound = false; 184 private boolean mIsBinding = false; 185 // Set of a pair of slotId->feature 186 private HashSet<ImsFeatureConfiguration.FeatureSlotPair> mImsFeatures; 187 // Binder interfaces to the features set in mImsFeatures; 188 private HashSet<ImsFeatureContainer> mImsFeatureBinders = new HashSet<>(); 189 private IImsServiceController mIImsServiceController; 190 private ImsServiceConnection mImsServiceConnection; 191 private Set<IImsServiceFeatureCallback> mImsStatusCallbacks = ConcurrentHashMap.newKeySet(); 192 // Only added or removed, never accessed on purpose. 193 private Set<ImsFeatureStatusCallback> mFeatureStatusCallbacks = new HashSet<>(); 194 195 protected final Object mLock = new Object(); 196 protected final Context mContext; 197 198 private class ImsFeatureContainer { 199 public int slotId; 200 public int featureType; 201 private IInterface mBinder; 202 ImsFeatureContainer(int slotId, int featureType, IInterface binder)203 ImsFeatureContainer(int slotId, int featureType, IInterface binder) { 204 this.slotId = slotId; 205 this.featureType = featureType; 206 this.mBinder = binder; 207 } 208 209 // Casts the IInterface into the binder class we are looking for. resolve(Class<T> className)210 public <T extends IInterface> T resolve(Class<T> className) { 211 return className.cast(mBinder); 212 } 213 214 @Override equals(Object o)215 public boolean equals(Object o) { 216 if (this == o) return true; 217 if (o == null || getClass() != o.getClass()) return false; 218 219 ImsFeatureContainer that = (ImsFeatureContainer) o; 220 221 if (slotId != that.slotId) return false; 222 if (featureType != that.featureType) return false; 223 return mBinder != null ? mBinder.equals(that.mBinder) : that.mBinder == null; 224 } 225 226 @Override hashCode()227 public int hashCode() { 228 int result = slotId; 229 result = 31 * result + featureType; 230 result = 31 * result + (mBinder != null ? mBinder.hashCode() : 0); 231 return result; 232 } 233 } 234 235 /** 236 * Container class for the IImsFeatureStatusCallback callback implementation. This class is 237 * never used directly, but we need to keep track of the IImsFeatureStatusCallback 238 * implementations explicitly. 239 */ 240 private class ImsFeatureStatusCallback { 241 private int mSlotId; 242 private int mFeatureType; 243 244 private final IImsFeatureStatusCallback mCallback = new IImsFeatureStatusCallback.Stub() { 245 246 @Override 247 public void notifyImsFeatureStatus(int featureStatus) throws RemoteException { 248 Log.i(LOG_TAG, "notifyImsFeatureStatus: slot=" + mSlotId + ", feature=" 249 + mFeatureType + ", status=" + featureStatus); 250 sendImsFeatureStatusChanged(mSlotId, mFeatureType, featureStatus); 251 } 252 }; 253 ImsFeatureStatusCallback(int slotId, int featureType)254 ImsFeatureStatusCallback(int slotId, int featureType) { 255 mSlotId = slotId; 256 mFeatureType = featureType; 257 } 258 getCallback()259 public IImsFeatureStatusCallback getCallback() { 260 return mCallback; 261 } 262 } 263 264 // Retry the bind to the ImsService that has died after mRebindRetry timeout. 265 private Runnable mRestartImsServiceRunnable = new Runnable() { 266 @Override 267 public void run() { 268 synchronized (mLock) { 269 if (mIsBound) { 270 return; 271 } 272 bind(mImsFeatures); 273 } 274 } 275 }; 276 277 private RebindRetry mRebindRetry = new RebindRetry() { 278 @Override 279 public long getStartDelay() { 280 return REBIND_START_DELAY_MS; 281 } 282 283 @Override 284 public long getMaximumDelay() { 285 return REBIND_MAXIMUM_DELAY_MS; 286 } 287 }; 288 ImsServiceController(Context context, ComponentName componentName, ImsServiceControllerCallbacks callbacks)289 public ImsServiceController(Context context, ComponentName componentName, 290 ImsServiceControllerCallbacks callbacks) { 291 mContext = context; 292 mComponentName = componentName; 293 mCallbacks = callbacks; 294 mHandlerThread.start(); 295 mBackoff = new ExponentialBackoff( 296 mRebindRetry.getStartDelay(), 297 mRebindRetry.getMaximumDelay(), 298 2, /* multiplier */ 299 mHandlerThread.getLooper(), 300 mRestartImsServiceRunnable); 301 mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package")); 302 } 303 304 @VisibleForTesting 305 // Creating a new HandlerThread and background handler for each test causes a segfault, so for 306 // testing, use a handler supplied by the testing system. ImsServiceController(Context context, ComponentName componentName, ImsServiceControllerCallbacks callbacks, Handler handler, RebindRetry rebindRetry)307 public ImsServiceController(Context context, ComponentName componentName, 308 ImsServiceControllerCallbacks callbacks, Handler handler, RebindRetry rebindRetry) { 309 mContext = context; 310 mComponentName = componentName; 311 mCallbacks = callbacks; 312 mBackoff = new ExponentialBackoff( 313 rebindRetry.getStartDelay(), 314 rebindRetry.getMaximumDelay(), 315 2, /* multiplier */ 316 handler, 317 mRestartImsServiceRunnable); 318 mPackageManager = null; 319 } 320 321 /** 322 * Sends request to bind to ImsService designated by the {@link ComponentName} with the feature 323 * set imsFeatureSet. 324 * 325 * @param imsFeatureSet a Set of Pairs that designate the slotId->featureId that need to be 326 * created once the service is bound. 327 * @return {@link true} if the service is in the process of being bound, {@link false} if it 328 * has failed. 329 */ bind(HashSet<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet)330 public boolean bind(HashSet<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet) { 331 synchronized (mLock) { 332 if (!mIsBound && !mIsBinding) { 333 mIsBinding = true; 334 mImsFeatures = imsFeatureSet; 335 grantPermissionsToService(); 336 Intent imsServiceIntent = new Intent(getServiceInterface()).setComponent( 337 mComponentName); 338 mImsServiceConnection = new ImsServiceConnection(); 339 int serviceFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE 340 | Context.BIND_IMPORTANT; 341 Log.i(LOG_TAG, "Binding ImsService:" + mComponentName); 342 try { 343 boolean bindSucceeded = startBindToService(imsServiceIntent, 344 mImsServiceConnection, serviceFlags); 345 if (!bindSucceeded) { 346 mIsBinding = false; 347 mBackoff.notifyFailed(); 348 } 349 return bindSucceeded; 350 } catch (Exception e) { 351 mBackoff.notifyFailed(); 352 Log.e(LOG_TAG, "Error binding (" + mComponentName + ") with exception: " 353 + e.getMessage() + ", rebinding in " + mBackoff.getCurrentDelay() 354 + " ms"); 355 return false; 356 } 357 } else { 358 return false; 359 } 360 } 361 } 362 363 /** 364 * Starts the bind to the ImsService. Overridden by subclasses that need to access the service 365 * in a different fashion. 366 */ startBindToService(Intent intent, ImsServiceConnection connection, int flags)367 protected boolean startBindToService(Intent intent, ImsServiceConnection connection, 368 int flags) { 369 return mContext.bindService(intent, connection, flags); 370 } 371 372 /** 373 * Calls {@link IImsServiceController#removeImsFeature} on all features that the 374 * ImsService supports and then unbinds the service. 375 */ unbind()376 public void unbind() throws RemoteException { 377 synchronized (mLock) { 378 mBackoff.stop(); 379 if (mImsServiceConnection == null) { 380 return; 381 } 382 // Clean up all features 383 changeImsServiceFeatures(new HashSet<>()); 384 removeImsServiceFeatureCallbacks(); 385 Log.i(LOG_TAG, "Unbinding ImsService: " + mComponentName); 386 mContext.unbindService(mImsServiceConnection); 387 cleanUpService(); 388 } 389 } 390 391 /** 392 * For every feature that is added, the service calls the associated create. For every 393 * ImsFeature that is removed, {@link IImsServiceController#removeImsFeature} is called. 394 */ changeImsServiceFeatures( HashSet<ImsFeatureConfiguration.FeatureSlotPair> newImsFeatures)395 public void changeImsServiceFeatures( 396 HashSet<ImsFeatureConfiguration.FeatureSlotPair> newImsFeatures) 397 throws RemoteException { 398 synchronized (mLock) { 399 Log.i(LOG_TAG, "Features changed (" + mImsFeatures + "->" + newImsFeatures + ") for " 400 + "ImsService: " + mComponentName); 401 HashSet<ImsFeatureConfiguration.FeatureSlotPair> oldImsFeatures = 402 new HashSet<>(mImsFeatures); 403 // Set features first in case we lose binding and need to rebind later. 404 mImsFeatures = newImsFeatures; 405 if (mIsBound) { 406 // add features to service. 407 HashSet<ImsFeatureConfiguration.FeatureSlotPair> newFeatures = 408 new HashSet<>(mImsFeatures); 409 newFeatures.removeAll(oldImsFeatures); 410 for (ImsFeatureConfiguration.FeatureSlotPair i : newFeatures) { 411 addImsServiceFeature(i); 412 } 413 // remove old features 414 HashSet<ImsFeatureConfiguration.FeatureSlotPair> oldFeatures = 415 new HashSet<>(oldImsFeatures); 416 oldFeatures.removeAll(mImsFeatures); 417 for (ImsFeatureConfiguration.FeatureSlotPair i : oldFeatures) { 418 removeImsServiceFeature(i); 419 } 420 } 421 } 422 } 423 424 @VisibleForTesting getImsServiceController()425 public IImsServiceController getImsServiceController() { 426 return mIImsServiceController; 427 } 428 429 @VisibleForTesting getRebindDelay()430 public long getRebindDelay() { 431 return mBackoff.getCurrentDelay(); 432 } 433 434 @VisibleForTesting stopBackoffTimerForTesting()435 public void stopBackoffTimerForTesting() { 436 mBackoff.stop(); 437 } 438 getComponentName()439 public ComponentName getComponentName() { 440 return mComponentName; 441 } 442 443 /** 444 * Add a callback to ImsManager that signals a new feature that the ImsServiceProxy can handle. 445 */ addImsServiceFeatureCallback(IImsServiceFeatureCallback callback)446 public void addImsServiceFeatureCallback(IImsServiceFeatureCallback callback) { 447 mImsStatusCallbacks.add(callback); 448 synchronized (mLock) { 449 if (mImsFeatures == null || mImsFeatures.isEmpty()) { 450 return; 451 } 452 // notify the new status callback of the features that are available. 453 try { 454 for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) { 455 callback.imsFeatureCreated(i.slotId, i.featureType); 456 } 457 } catch (RemoteException e) { 458 Log.w(LOG_TAG, "addImsServiceFeatureCallback: exception notifying callback"); 459 } 460 } 461 } 462 enableIms(int slotId)463 public void enableIms(int slotId) { 464 try { 465 synchronized (mLock) { 466 if (isServiceControllerAvailable()) { 467 mIImsServiceController.enableIms(slotId); 468 } 469 } 470 } catch (RemoteException e) { 471 Log.w(LOG_TAG, "Couldn't enable IMS: " + e.getMessage()); 472 } 473 } 474 disableIms(int slotId)475 public void disableIms(int slotId) { 476 try { 477 synchronized (mLock) { 478 if (isServiceControllerAvailable()) { 479 mIImsServiceController.disableIms(slotId); 480 } 481 } 482 } catch (RemoteException e) { 483 Log.w(LOG_TAG, "Couldn't disable IMS: " + e.getMessage()); 484 } 485 } 486 487 /** 488 * Return the {@Link MMTelFeature} binder on the slot associated with the slotId. 489 * Used for normal calling. 490 */ getMmTelFeature(int slotId)491 public IImsMmTelFeature getMmTelFeature(int slotId) { 492 synchronized (mLock) { 493 ImsFeatureContainer f = getImsFeatureContainer(slotId, ImsFeature.FEATURE_MMTEL); 494 if (f == null) { 495 Log.w(LOG_TAG, "Requested null MMTelFeature on slot " + slotId); 496 return null; 497 } 498 return f.resolve(IImsMmTelFeature.class); 499 } 500 } 501 502 /** 503 * Return the {@Link RcsFeature} binder on the slot associated with the slotId. 504 */ getRcsFeature(int slotId)505 public IImsRcsFeature getRcsFeature(int slotId) { 506 synchronized (mLock) { 507 ImsFeatureContainer f = getImsFeatureContainer(slotId, ImsFeature.FEATURE_RCS); 508 if (f == null) { 509 Log.w(LOG_TAG, "Requested null RcsFeature on slot " + slotId); 510 return null; 511 } 512 return f.resolve(IImsRcsFeature.class); 513 } 514 } 515 516 /** 517 * @return the IImsRegistration that corresponds to the slot id specified. 518 */ getRegistration(int slotId)519 public IImsRegistration getRegistration(int slotId) throws RemoteException { 520 synchronized (mLock) { 521 return isServiceControllerAvailable() 522 ? mIImsServiceController.getRegistration(slotId) : null; 523 } 524 } 525 526 /** 527 * @return the IImsConfig that corresponds to the slot id specified. 528 */ getConfig(int slotId)529 public IImsConfig getConfig(int slotId) throws RemoteException { 530 synchronized (mLock) { 531 return isServiceControllerAvailable() ? mIImsServiceController.getConfig(slotId) : null; 532 } 533 } 534 535 /** 536 * notify the ImsService that the ImsService is ready for feature creation. 537 */ notifyImsServiceReady()538 protected void notifyImsServiceReady() throws RemoteException { 539 synchronized (mLock) { 540 if (isServiceControllerAvailable()) { 541 Log.d(LOG_TAG, "notifyImsServiceReady"); 542 mIImsServiceController.setListener(mFeatureChangedListener); 543 mIImsServiceController.notifyImsServiceReadyForFeatureCreation(); 544 } 545 } 546 } 547 getServiceInterface()548 protected String getServiceInterface() { 549 return ImsService.SERVICE_INTERFACE; 550 } 551 552 /** 553 * Sets the IImsServiceController instance. Overridden by compat layers to set compatibility 554 * versions of this service controller. 555 */ setServiceController(IBinder serviceController)556 protected void setServiceController(IBinder serviceController) { 557 mIImsServiceController = IImsServiceController.Stub.asInterface(serviceController); 558 } 559 560 /** 561 * @return true if the controller is currently bound. 562 */ isBound()563 public boolean isBound() { 564 synchronized (mLock) { 565 return mIsBound; 566 } 567 } 568 569 /** 570 * Check to see if the service controller is available, overridden for compat versions, 571 * @return true if available, false otherwise; 572 */ isServiceControllerAvailable()573 protected boolean isServiceControllerAvailable() { 574 return mIImsServiceController != null; 575 } 576 577 @VisibleForTesting removeImsServiceFeatureCallbacks()578 public void removeImsServiceFeatureCallbacks() { 579 mImsStatusCallbacks.clear(); 580 } 581 582 // Only add a new rebind if there are no pending rebinds waiting. startDelayedRebindToService()583 private void startDelayedRebindToService() { 584 mBackoff.start(); 585 } 586 587 // Grant runtime permissions to ImsService. PackageManager ensures that the ImsService is 588 // system/signed before granting permissions. grantPermissionsToService()589 private void grantPermissionsToService() { 590 Log.i(LOG_TAG, "Granting Runtime permissions to:" + getComponentName()); 591 String[] pkgToGrant = {mComponentName.getPackageName()}; 592 try { 593 if (mPackageManager != null) { 594 mPackageManager.grantDefaultPermissionsToEnabledImsServices(pkgToGrant, 595 mContext.getUserId()); 596 } 597 } catch (RemoteException e) { 598 Log.w(LOG_TAG, "Unable to grant permissions, binder died."); 599 } 600 } 601 sendImsFeatureCreatedCallback(int slot, int feature)602 private void sendImsFeatureCreatedCallback(int slot, int feature) { 603 for (Iterator<IImsServiceFeatureCallback> i = mImsStatusCallbacks.iterator(); 604 i.hasNext(); ) { 605 IImsServiceFeatureCallback callbacks = i.next(); 606 try { 607 callbacks.imsFeatureCreated(slot, feature); 608 } catch (RemoteException e) { 609 // binder died, remove callback. 610 Log.w(LOG_TAG, "sendImsFeatureCreatedCallback: Binder died, removing " 611 + "callback. Exception:" + e.getMessage()); 612 i.remove(); 613 } 614 } 615 } 616 sendImsFeatureRemovedCallback(int slot, int feature)617 private void sendImsFeatureRemovedCallback(int slot, int feature) { 618 for (Iterator<IImsServiceFeatureCallback> i = mImsStatusCallbacks.iterator(); 619 i.hasNext(); ) { 620 IImsServiceFeatureCallback callbacks = i.next(); 621 try { 622 callbacks.imsFeatureRemoved(slot, feature); 623 } catch (RemoteException e) { 624 // binder died, remove callback. 625 Log.w(LOG_TAG, "sendImsFeatureRemovedCallback: Binder died, removing " 626 + "callback. Exception:" + e.getMessage()); 627 i.remove(); 628 } 629 } 630 } 631 sendImsFeatureStatusChanged(int slot, int feature, int status)632 private void sendImsFeatureStatusChanged(int slot, int feature, int status) { 633 for (Iterator<IImsServiceFeatureCallback> i = mImsStatusCallbacks.iterator(); 634 i.hasNext(); ) { 635 IImsServiceFeatureCallback callbacks = i.next(); 636 try { 637 callbacks.imsStatusChanged(slot, feature, status); 638 } catch (RemoteException e) { 639 // binder died, remove callback. 640 Log.w(LOG_TAG, "sendImsFeatureStatusChanged: Binder died, removing " 641 + "callback. Exception:" + e.getMessage()); 642 i.remove(); 643 } 644 } 645 } 646 647 // This method should only be called when synchronized on mLock addImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair)648 private void addImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair) 649 throws RemoteException { 650 if (!isServiceControllerAvailable() || mCallbacks == null) { 651 Log.w(LOG_TAG, "addImsServiceFeature called with null values."); 652 return; 653 } 654 if (featurePair.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL) { 655 ImsFeatureStatusCallback c = new ImsFeatureStatusCallback(featurePair.slotId, 656 featurePair.featureType); 657 mFeatureStatusCallbacks.add(c); 658 IInterface f = createImsFeature(featurePair.slotId, featurePair.featureType, 659 c.getCallback()); 660 addImsFeatureBinder(featurePair.slotId, featurePair.featureType, f); 661 // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController 662 mCallbacks.imsServiceFeatureCreated(featurePair.slotId, featurePair.featureType, this); 663 } else { 664 // Don't update ImsService for emergency MMTEL feature. 665 Log.i(LOG_TAG, "supports emergency calling on slot " + featurePair.slotId); 666 } 667 // Send callback to ImsServiceProxy to change supported ImsFeatures including emergency 668 // MMTEL state. 669 sendImsFeatureCreatedCallback(featurePair.slotId, featurePair.featureType); 670 } 671 672 // This method should only be called when synchronized on mLock removeImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair)673 private void removeImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair) { 674 if (!isServiceControllerAvailable() || mCallbacks == null) { 675 Log.w(LOG_TAG, "removeImsServiceFeature called with null values."); 676 return; 677 } 678 if (featurePair.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL) { 679 ImsFeatureStatusCallback callbackToRemove = mFeatureStatusCallbacks.stream().filter(c -> 680 c.mSlotId == featurePair.slotId && c.mFeatureType == featurePair.featureType) 681 .findFirst().orElse(null); 682 // Remove status callbacks from list. 683 if (callbackToRemove != null) { 684 mFeatureStatusCallbacks.remove(callbackToRemove); 685 } 686 removeImsFeatureBinder(featurePair.slotId, featurePair.featureType); 687 // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController 688 mCallbacks.imsServiceFeatureRemoved(featurePair.slotId, featurePair.featureType, this); 689 try { 690 removeImsFeature(featurePair.slotId, featurePair.featureType, 691 (callbackToRemove != null ? callbackToRemove.getCallback() : null)); 692 } catch (RemoteException e) { 693 // The connection to this ImsService doesn't exist. This may happen if the service 694 // has died and we are removing features. 695 Log.i(LOG_TAG, "Couldn't remove feature {" + featurePair.featureType 696 + "}, connection is down: " + e.getMessage()); 697 } 698 } else { 699 // Don't update ImsService for emergency MMTEL feature. 700 Log.i(LOG_TAG, "doesn't support emergency calling on slot " + featurePair.slotId); 701 } 702 // Send callback to ImsServiceProxy to change supported ImsFeatures 703 // Ensure that ImsServiceProxy callback occurs after ImsResolver callback. If an 704 // ImsManager requests the ImsService while it is being removed in ImsResolver, this 705 // callback will clean it up after. 706 sendImsFeatureRemovedCallback(featurePair.slotId, featurePair.featureType); 707 } 708 709 // This method should only be called when already synchronized on mLock. 710 // overridden by compat layer to create features createImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)711 protected IInterface createImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c) 712 throws RemoteException { 713 switch (featureType) { 714 case ImsFeature.FEATURE_MMTEL: { 715 return mIImsServiceController.createMmTelFeature(slotId, c); 716 } 717 case ImsFeature.FEATURE_RCS: { 718 return mIImsServiceController.createRcsFeature(slotId, c); 719 } 720 default: 721 return null; 722 } 723 } 724 725 // overridden by compat layer to remove features removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)726 protected void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c) 727 throws RemoteException { 728 mIImsServiceController.removeImsFeature(slotId, featureType, c); 729 } 730 731 // This method should only be called when synchronized on mLock addImsFeatureBinder(int slotId, int featureType, IInterface b)732 private void addImsFeatureBinder(int slotId, int featureType, IInterface b) { 733 mImsFeatureBinders.add(new ImsFeatureContainer(slotId, featureType, b)); 734 } 735 736 // This method should only be called when synchronized on mLock removeImsFeatureBinder(int slotId, int featureType)737 private void removeImsFeatureBinder(int slotId, int featureType) { 738 ImsFeatureContainer container = mImsFeatureBinders.stream() 739 .filter(f-> (f.slotId == slotId && f.featureType == featureType)) 740 .findFirst().orElse(null); 741 if (container != null) { 742 mImsFeatureBinders.remove(container); 743 } 744 } 745 getImsFeatureContainer(int slotId, int featureType)746 private ImsFeatureContainer getImsFeatureContainer(int slotId, int featureType) { 747 return mImsFeatureBinders.stream() 748 .filter(f-> (f.slotId == slotId && f.featureType == featureType)) 749 .findFirst().orElse(null); 750 } 751 cleanupAllFeatures()752 private void cleanupAllFeatures() { 753 synchronized (mLock) { 754 // Remove all features and clean up all associated Binders. 755 for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) { 756 removeImsServiceFeature(i); 757 } 758 // remove all MmTelFeatureConnection callbacks, since we have already sent removed 759 // callback. 760 removeImsServiceFeatureCallbacks(); 761 } 762 } 763 cleanUpService()764 private void cleanUpService() { 765 synchronized (mLock) { 766 mImsServiceConnection = null; 767 setServiceController(null); 768 } 769 } 770 } 771