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