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 android.telephony.ims; 18 19 import android.annotation.LongDef; 20 import android.annotation.Nullable; 21 import android.annotation.SuppressLint; 22 import android.annotation.SystemApi; 23 import android.app.Service; 24 import android.content.Intent; 25 import android.os.IBinder; 26 import android.os.RemoteException; 27 import android.telephony.CarrierConfigManager; 28 import android.telephony.ims.aidl.IImsConfig; 29 import android.telephony.ims.aidl.IImsMmTelFeature; 30 import android.telephony.ims.aidl.IImsRcsFeature; 31 import android.telephony.ims.aidl.IImsRegistration; 32 import android.telephony.ims.aidl.IImsServiceController; 33 import android.telephony.ims.aidl.IImsServiceControllerListener; 34 import android.telephony.ims.aidl.ISipTransport; 35 import android.telephony.ims.feature.ImsFeature; 36 import android.telephony.ims.feature.MmTelFeature; 37 import android.telephony.ims.feature.RcsFeature; 38 import android.telephony.ims.stub.ImsConfigImplBase; 39 import android.telephony.ims.stub.ImsFeatureConfiguration; 40 import android.telephony.ims.stub.ImsRegistrationImplBase; 41 import android.telephony.ims.stub.SipTransportImplBase; 42 import android.util.Log; 43 import android.util.SparseArray; 44 45 import com.android.ims.internal.IImsFeatureStatusCallback; 46 import com.android.internal.annotations.VisibleForTesting; 47 48 import java.lang.annotation.Retention; 49 import java.lang.annotation.RetentionPolicy; 50 import java.util.HashMap; 51 import java.util.Map; 52 53 /** 54 * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend 55 * ImsService must register the service in their AndroidManifest to be detected by the framework. 56 * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE" 57 * permission. Then, the ImsService definition in the manifest must follow the following format: 58 * 59 * ... 60 * <service android:name=".EgImsService" 61 * android:permission="android.permission.BIND_IMS_SERVICE" > 62 * ... 63 * <intent-filter> 64 * <action android:name="android.telephony.ims.ImsService" /> 65 * </intent-filter> 66 * </service> 67 * ... 68 * 69 * The telephony framework will then bind to the ImsService you have defined in your manifest 70 * if you are either: 71 * 1) Defined as the default ImsService for the device in the device overlay using 72 * "config_ims_mmtel_package" or "config_ims_rcs_package". 73 * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using 74 * {@link CarrierConfigManager#KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING} or 75 * {@link CarrierConfigManager#KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING}. 76 * 77 * There are two ways to define to the platform which {@link ImsFeature}s this {@link ImsService} 78 * supports, dynamic or static definitions. 79 * 80 * In the static definition, the {@link ImsFeature}s that are supported are defined in the service 81 * definition of the AndroidManifest.xml file as metadata: 82 * <!-- Apps must declare which features they support as metadata. The different categories are 83 * defined below. In this example, the MMTEL_FEATURE feature is supported. --> 84 * <meta-data android:name="android.telephony.ims.MMTEL_FEATURE" android:value="true" /> 85 * 86 * The features that are currently supported in an ImsService are: 87 * - RCS_FEATURE: This ImsService implements the RcsFeature class. 88 * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class. 89 * - EMERGENCY_MMTEL_FEATURE: This ImsService supports Emergency Calling for MMTEL, must be 90 * declared along with the MMTEL_FEATURE. If this is not specified, the framework will use 91 * circuit switch for emergency calling. 92 * 93 * In the dynamic definition, the supported features are not specified in the service definition 94 * of the AndroidManifest. Instead, the framework binds to this service and calls 95 * {@link #querySupportedImsFeatures()}. The {@link ImsService} then returns an 96 * {@link ImsFeatureConfiguration}, which the framework uses to initialize the supported 97 * {@link ImsFeature}s. If at any time, the list of supported {@link ImsFeature}s changes, 98 * {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} can be called to tell the 99 * framework of the changes. 100 * 101 * @hide 102 */ 103 @SystemApi 104 public class ImsService extends Service { 105 106 private static final String LOG_TAG = "ImsService"; 107 108 /** 109 * This ImsService supports the capability to place emergency calls over MMTEL. 110 * <p> 111 * Note: This should never be set by {@link #getImsServiceCapabilities()}, as whether it is 112 * there or not depends on whether or not {@link ImsFeature#FEATURE_EMERGENCY_MMTEL} is defined 113 * for this ImsService. If it is set, it will be removed during sanitization before the final 114 * capabilities bitfield is sent back to the framework. 115 * @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be 116 * adding other capabilities in a central location, so track this capability here as well. 117 */ 118 public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0; 119 120 /** 121 * This ImsService supports the capability to create SIP delegates for other IMS applications 122 * to use to proxy SIP messaging traffic through it. 123 * <p> 124 * In order for the framework to report SipDelegate creation as being available for this 125 * ImsService implementation, this ImsService must report this capability flag in 126 * {@link #getImsServiceCapabilities()}, {@link #getSipTransport(int)} must not return null, and 127 * this ImsService MUST report the ability to create both {@link ImsFeature#FEATURE_MMTEL} and 128 * {@link ImsFeature#FEATURE_RCS} features. 129 */ 130 public static final long CAPABILITY_SIP_DELEGATE_CREATION = 1 << 1; 131 132 /** 133 * Used for internal correctness checks of capabilities set by the ImsService implementation and 134 * tracks the index of the largest defined flag in the capabilities long. 135 * @hide 136 */ 137 public static final long CAPABILITY_MAX_INDEX = 138 Long.numberOfTrailingZeros(CAPABILITY_SIP_DELEGATE_CREATION); 139 140 /** 141 * @hide 142 */ 143 @LongDef(flag = true, 144 prefix = "CAPABILITY_", 145 value = { 146 // CAPABILITY_EMERGENCY_OVER_MMTEL is not included here because it is managed by 147 // whether or not ImsFeature.FEATURE_EMERGENCY_MMTEL feature is set and should 148 // not be set by users of ImsService. 149 CAPABILITY_SIP_DELEGATE_CREATION 150 }) 151 @Retention(RetentionPolicy.SOURCE) 152 public @interface ImsServiceCapability {} 153 154 /** 155 * Used for logging purposes, see {@link #getCapabilitiesString(long)} 156 * @hide 157 */ 158 private static final Map<Long, String> CAPABILITIES_LOG_MAP = new HashMap<Long, String>() {{ 159 put(CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL"); 160 put(CAPABILITY_SIP_DELEGATE_CREATION, "SIP_DELEGATE_CREATION"); 161 }}; 162 163 /** 164 * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService. 165 * @hide 166 */ 167 public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService"; 168 169 // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that 170 // slot. 171 // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and 172 // call ImsFeature#onFeatureRemoved. 173 private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>(); 174 175 private IImsServiceControllerListener mListener; 176 177 178 /** 179 * Listener that notifies the framework of ImsService changes. 180 * @hide 181 */ 182 public static class Listener extends IImsServiceControllerListener.Stub { 183 /** 184 * The IMS features that this ImsService supports has changed. 185 * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s 186 * that this ImsService supports. This may trigger the addition/removal of feature 187 * in this service. 188 */ onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)189 public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) { 190 } 191 } 192 193 /** 194 * @hide 195 */ 196 protected final IBinder mImsServiceController = new IImsServiceController.Stub() { 197 @Override 198 public void setListener(IImsServiceControllerListener l) { 199 mListener = l; 200 } 201 202 @Override 203 public IImsMmTelFeature createMmTelFeature(int slotId) { 204 return createMmTelFeatureInternal(slotId); 205 } 206 207 @Override 208 public IImsRcsFeature createRcsFeature(int slotId) { 209 return createRcsFeatureInternal(slotId); 210 } 211 212 @Override 213 public void addFeatureStatusCallback(int slotId, int featureType, 214 IImsFeatureStatusCallback c) { 215 ImsService.this.addImsFeatureStatusCallback(slotId, featureType, c); 216 } 217 218 @Override 219 public void removeFeatureStatusCallback(int slotId, int featureType, 220 IImsFeatureStatusCallback c) { 221 ImsService.this.removeImsFeatureStatusCallback(slotId, featureType, c); 222 } 223 224 @Override 225 public void removeImsFeature(int slotId, int featureType) { 226 ImsService.this.removeImsFeature(slotId, featureType); 227 } 228 229 @Override 230 public ImsFeatureConfiguration querySupportedImsFeatures() { 231 return ImsService.this.querySupportedImsFeatures(); 232 } 233 234 @Override 235 public long getImsServiceCapabilities() { 236 long caps = ImsService.this.getImsServiceCapabilities(); 237 long sanitizedCaps = sanitizeCapabilities(caps); 238 if (caps != sanitizedCaps) { 239 Log.w(LOG_TAG, "removing invalid bits from field: 0x" 240 + Long.toHexString(caps ^ sanitizedCaps)); 241 } 242 return sanitizedCaps; 243 } 244 245 @Override 246 public void notifyImsServiceReadyForFeatureCreation() { 247 ImsService.this.readyForFeatureCreation(); 248 } 249 250 @Override 251 public IImsConfig getConfig(int slotId) { 252 ImsConfigImplBase c = ImsService.this.getConfig(slotId); 253 return c != null ? c.getIImsConfig() : null; 254 } 255 256 @Override 257 public IImsRegistration getRegistration(int slotId) { 258 ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId); 259 return r != null ? r.getBinder() : null; 260 } 261 262 @Override 263 public ISipTransport getSipTransport(int slotId) { 264 SipTransportImplBase s = ImsService.this.getSipTransport(slotId); 265 return s != null ? s.getBinder() : null; 266 } 267 268 @Override 269 public void enableIms(int slotId) { 270 ImsService.this.enableIms(slotId); 271 } 272 273 @Override 274 public void disableIms(int slotId) { 275 ImsService.this.disableIms(slotId); 276 } 277 }; 278 279 /** 280 * @hide 281 */ 282 @Override onBind(Intent intent)283 public IBinder onBind(Intent intent) { 284 if(SERVICE_INTERFACE.equals(intent.getAction())) { 285 Log.i(LOG_TAG, "ImsService Bound."); 286 return mImsServiceController; 287 } 288 return null; 289 } 290 291 /** 292 * @hide 293 */ 294 @VisibleForTesting getFeatures(int slotId)295 public SparseArray<ImsFeature> getFeatures(int slotId) { 296 return mFeaturesBySlot.get(slotId); 297 } 298 createMmTelFeatureInternal(int slotId)299 private IImsMmTelFeature createMmTelFeatureInternal(int slotId) { 300 MmTelFeature f = createMmTelFeature(slotId); 301 if (f != null) { 302 setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL); 303 return f.getBinder(); 304 } else { 305 Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned."); 306 return null; 307 } 308 } 309 createRcsFeatureInternal(int slotId)310 private IImsRcsFeature createRcsFeatureInternal(int slotId) { 311 RcsFeature f = createRcsFeature(slotId); 312 if (f != null) { 313 setupFeature(f, slotId, ImsFeature.FEATURE_RCS); 314 return f.getBinder(); 315 } else { 316 Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned."); 317 return null; 318 } 319 } 320 setupFeature(ImsFeature f, int slotId, int featureType)321 private void setupFeature(ImsFeature f, int slotId, int featureType) { 322 f.initialize(this, slotId); 323 addImsFeature(slotId, featureType, f); 324 } 325 addImsFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c)326 private void addImsFeatureStatusCallback(int slotId, int featureType, 327 IImsFeatureStatusCallback c) { 328 synchronized (mFeaturesBySlot) { 329 // get ImsFeature associated with the slot/feature 330 SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); 331 if (features == null) { 332 Log.w(LOG_TAG, "Can not add ImsFeatureStatusCallback - no features on slot " 333 + slotId); 334 return; 335 } 336 ImsFeature f = features.get(featureType); 337 if (f != null) { 338 f.addImsFeatureStatusCallback(c); 339 } 340 } 341 } 342 removeImsFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c)343 private void removeImsFeatureStatusCallback(int slotId, int featureType, 344 IImsFeatureStatusCallback c) { 345 synchronized (mFeaturesBySlot) { 346 // get ImsFeature associated with the slot/feature 347 SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); 348 if (features == null) { 349 Log.w(LOG_TAG, "Can not remove ImsFeatureStatusCallback - no features on slot " 350 + slotId); 351 return; 352 } 353 ImsFeature f = features.get(featureType); 354 if (f != null) { 355 f.removeImsFeatureStatusCallback(c); 356 } 357 } 358 } 359 addImsFeature(int slotId, int featureType, ImsFeature f)360 private void addImsFeature(int slotId, int featureType, ImsFeature f) { 361 synchronized (mFeaturesBySlot) { 362 // Get SparseArray for Features, by querying slot Id 363 SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); 364 if (features == null) { 365 // Populate new SparseArray of features if it doesn't exist for this slot yet. 366 features = new SparseArray<>(); 367 mFeaturesBySlot.put(slotId, features); 368 } 369 features.put(featureType, f); 370 } 371 } 372 removeImsFeature(int slotId, int featureType)373 private void removeImsFeature(int slotId, int featureType) { 374 synchronized (mFeaturesBySlot) { 375 // get ImsFeature associated with the slot/feature 376 SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); 377 if (features == null) { 378 Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot " 379 + slotId); 380 return; 381 } 382 ImsFeature f = features.get(featureType); 383 if (f == null) { 384 Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type " 385 + featureType + " exists on slot " + slotId); 386 return; 387 } 388 f.onFeatureRemoved(); 389 features.remove(featureType); 390 } 391 } 392 393 /** 394 * When called, provide the {@link ImsFeatureConfiguration} that this {@link ImsService} 395 * currently supports. This will trigger the framework to set up the {@link ImsFeature}s that 396 * correspond to the {@link ImsFeature}s configured here. 397 * 398 * Use {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} to change the supported 399 * {@link ImsFeature}s. 400 * 401 * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports. 402 */ querySupportedImsFeatures()403 public ImsFeatureConfiguration querySupportedImsFeatures() { 404 // Return empty for base implementation 405 return new ImsFeatureConfiguration(); 406 } 407 408 /** 409 * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated 410 * features, that this {@link ImsService} supports. This may trigger the framework to add/remove 411 * new ImsFeatures, depending on the configuration. 412 */ onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)413 public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) 414 throws RemoteException { 415 if (mListener == null) { 416 throw new IllegalStateException("Framework is not ready"); 417 } 418 mListener.onUpdateSupportedImsFeatures(c); 419 } 420 421 /** 422 * The optional capabilities that this ImsService supports. 423 * <p> 424 * This should be a static configuration and should not change at runtime. 425 * @return The optional static capabilities of this ImsService implementation. 426 */ 427 // ImsService follows a different convention, since it is a stub class. The on* methods are 428 // final and call back into the framework with a state update. 429 @SuppressLint("OnNameExpected") getImsServiceCapabilities()430 public @ImsServiceCapability long getImsServiceCapabilities() { 431 // Stub implementation to be implemented by ImsService. 432 return 0L; 433 } 434 435 /** 436 * The ImsService has been bound and is ready for ImsFeature creation based on the Features that 437 * the ImsService has registered for with the framework, either in the manifest or via 438 * {@link #querySupportedImsFeatures()}. 439 * 440 * The ImsService should use this signal instead of onCreate/onBind or similar to perform 441 * feature initialization because the framework may bind to this service multiple times to 442 * query the ImsService's {@link ImsFeatureConfiguration} via 443 * {@link #querySupportedImsFeatures()}before creating features. 444 */ readyForFeatureCreation()445 public void readyForFeatureCreation() { 446 } 447 448 /** 449 * The framework has enabled IMS for the slot specified, the ImsService should register for IMS 450 * and perform all appropriate initialization to bring up all ImsFeatures. 451 */ enableIms(int slotId)452 public void enableIms(int slotId) { 453 } 454 455 /** 456 * The framework has disabled IMS for the slot specified. The ImsService must deregister for IMS 457 * and set capability status to false for all ImsFeatures. 458 */ disableIms(int slotId)459 public void disableIms(int slotId) { 460 } 461 462 /** 463 * When called, the framework is requesting that a new {@link MmTelFeature} is created for the 464 * specified slot. 465 * 466 * @param slotId The slot ID that the MMTEL Feature is being created for. 467 * @return The newly created {@link MmTelFeature} associated with the slot or null if the 468 * feature is not supported. 469 */ createMmTelFeature(int slotId)470 public MmTelFeature createMmTelFeature(int slotId) { 471 return null; 472 } 473 474 /** 475 * When called, the framework is requesting that a new {@link RcsFeature} is created for the 476 * specified slot. 477 * 478 * @param slotId The slot ID that the RCS Feature is being created for. 479 * @return The newly created {@link RcsFeature} associated with the slot or null if the feature 480 * is not supported. 481 */ createRcsFeature(int slotId)482 public RcsFeature createRcsFeature(int slotId) { 483 return null; 484 } 485 486 /** 487 * Return the {@link ImsConfigImplBase} implementation associated with the provided slot. This 488 * will be used by the platform to get/set specific IMS related configurations. 489 * 490 * @param slotId The slot that the IMS configuration is associated with. 491 * @return ImsConfig implementation that is associated with the specified slot. 492 */ getConfig(int slotId)493 public ImsConfigImplBase getConfig(int slotId) { 494 return new ImsConfigImplBase(); 495 } 496 497 /** 498 * Return the {@link ImsRegistrationImplBase} implementation associated with the provided slot. 499 * 500 * @param slotId The slot that is associated with the IMS Registration. 501 * @return the ImsRegistration implementation associated with the slot. 502 */ getRegistration(int slotId)503 public ImsRegistrationImplBase getRegistration(int slotId) { 504 return new ImsRegistrationImplBase(); 505 } 506 507 /** 508 * Return the {@link SipTransportImplBase} implementation associated with the provided slot. 509 * <p> 510 * This is an optional interface used for devices that must support IMS single registration and 511 * proxy SIP traffic to remote IMS applications. If this is not supported for this IMS service, 512 * this method should return {@code null}. If this feature is supported, then this method must 513 * never be {@code null} and the optional ImsService capability flag 514 * {@link #CAPABILITY_SIP_DELEGATE_CREATION} must be set in 515 * {@link #getImsServiceCapabilities()}. Otherwise the framework will assume this feature is not 516 * supported for this ImsService. 517 * @param slotId The slot that is associated with the SipTransport implementation. 518 * @return the SipTransport implementation for the specified slot. 519 */ 520 // ImsService follows a different convention, since it is a stub class. The on* methods are 521 // final and call back into the framework with a state update. 522 @SuppressLint("OnNameExpected") getSipTransport(int slotId)523 public @Nullable SipTransportImplBase getSipTransport(int slotId) { 524 // Stub implementation for ImsServices that do not support SipTransport. 525 return null; 526 } 527 sanitizeCapabilities(@msServiceCapability long caps)528 private static long sanitizeCapabilities(@ImsServiceCapability long caps) { 529 long filter = 0xFFFFFFFFFFFFFFFFL; 530 // pad the "allowed" set with zeros 531 filter <<= CAPABILITY_MAX_INDEX + 1; 532 // remove values above the allowed set. 533 caps &= ~filter; 534 // CAPABILITY_EMERGENCY_OVER_MMTEL should also not be set here, will be set by telephony 535 // internally. 536 caps &= ~CAPABILITY_EMERGENCY_OVER_MMTEL; 537 return caps; 538 } 539 540 /** 541 * @return A string representation of the ImsService capabilities for logging. 542 * @hide 543 */ getCapabilitiesString(@msServiceCapability long caps)544 public static String getCapabilitiesString(@ImsServiceCapability long caps) { 545 StringBuffer result = new StringBuffer(); 546 result.append("capabilities={ "); 547 // filter incrementally fills 0s from left to right. This is used to keep filtering out 548 // more bits in the long until the remaining leftmost bits are all zero. 549 long filter = 0xFFFFFFFFFFFFFFFFL; 550 // position of iterator to potentially print capability. 551 long i = 0; 552 while ((caps & filter) != 0 && i <= 63) { 553 long bitToCheck = (1L << i); 554 if ((caps & bitToCheck) != 0) { 555 result.append(CAPABILITIES_LOG_MAP.getOrDefault(bitToCheck, bitToCheck + "?")); 556 result.append(" "); 557 } 558 // shift left by one and fill in another 1 on the leftmost bit. 559 filter <<= 1; 560 i++; 561 } 562 result.append("}"); 563 return result.toString(); 564 } 565 } 566