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