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