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.SystemApi; 20 import android.app.Service; 21 import android.content.Intent; 22 import android.os.IBinder; 23 import android.os.RemoteException; 24 import android.telephony.CarrierConfigManager; 25 import android.telephony.ims.aidl.IImsConfig; 26 import android.telephony.ims.aidl.IImsMmTelFeature; 27 import android.telephony.ims.aidl.IImsRcsFeature; 28 import android.telephony.ims.aidl.IImsRegistration; 29 import android.telephony.ims.aidl.IImsServiceController; 30 import android.telephony.ims.aidl.IImsServiceControllerListener; 31 import android.telephony.ims.feature.ImsFeature; 32 import android.telephony.ims.feature.MmTelFeature; 33 import android.telephony.ims.feature.RcsFeature; 34 import android.telephony.ims.stub.ImsConfigImplBase; 35 import android.telephony.ims.stub.ImsFeatureConfiguration; 36 import android.telephony.ims.stub.ImsRegistrationImplBase; 37 import android.util.Log; 38 import android.util.SparseArray; 39 40 import com.android.ims.internal.IImsFeatureStatusCallback; 41 import com.android.internal.annotations.VisibleForTesting; 42 43 import static android.Manifest.permission.MODIFY_PHONE_STATE; 44 45 /** 46 * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend 47 * ImsService must register the service in their AndroidManifest to be detected by the framework. 48 * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE" 49 * permission. Then, the ImsService definition in the manifest must follow the following format: 50 * 51 * ... 52 * <service android:name=".EgImsService" 53 * android:permission="android.permission.BIND_IMS_SERVICE" > 54 * ... 55 * <intent-filter> 56 * <action android:name="android.telephony.ims.ImsService" /> 57 * </intent-filter> 58 * </service> 59 * ... 60 * 61 * The telephony framework will then bind to the ImsService you have defined in your manifest 62 * if you are either: 63 * 1) Defined as the default ImsService for the device in the device overlay using 64 * "config_ims_package". 65 * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using 66 * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}. 67 * 68 * There are two ways to define to the platform which {@link ImsFeature}s this {@link ImsService} 69 * supports, dynamic or static definitions. 70 * 71 * In the static definition, the {@link ImsFeature}s that are supported are defined in the service 72 * definition of the AndroidManifest.xml file as metadata: 73 * <!-- Apps must declare which features they support as metadata. The different categories are 74 * defined below. In this example, the MMTEL_FEATURE feature is supported. --> 75 * <meta-data android:name="android.telephony.ims.MMTEL_FEATURE" android:value="true" /> 76 * 77 * The features that are currently supported in an ImsService are: 78 * - RCS_FEATURE: This ImsService implements the RcsFeature class. 79 * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class. 80 * - EMERGENCY_MMTEL_FEATURE: This ImsService supports Emergency Calling for MMTEL, must be 81 * declared along with the MMTEL_FEATURE. If this is not specified, the framework will use 82 * circuit switch for emergency calling. 83 * 84 * In the dynamic definition, the supported features are not specified in the service definition 85 * of the AndroidManifest. Instead, the framework binds to this service and calls 86 * {@link #querySupportedImsFeatures()}. The {@link ImsService} then returns an 87 * {@link ImsFeatureConfiguration}, which the framework uses to initialize the supported 88 * {@link ImsFeature}s. If at any time, the list of supported {@link ImsFeature}s changes, 89 * {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} can be called to tell the 90 * framework of the changes. 91 * 92 * @hide 93 */ 94 @SystemApi 95 public class ImsService extends Service { 96 97 private static final String LOG_TAG = "ImsService"; 98 99 /** 100 * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService. 101 * @hide 102 */ 103 public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService"; 104 105 // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that 106 // slot. 107 // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and 108 // call ImsFeature#onFeatureRemoved. 109 private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>(); 110 111 private IImsServiceControllerListener mListener; 112 113 114 /** 115 * Listener that notifies the framework of ImsService changes. 116 * @hide 117 */ 118 public static class Listener extends IImsServiceControllerListener.Stub { 119 /** 120 * The IMS features that this ImsService supports has changed. 121 * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s 122 * that this ImsService supports. This may trigger the addition/removal of feature 123 * in this service. 124 */ 125 public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) { 126 } 127 } 128 129 /** 130 * @hide 131 */ 132 protected final IBinder mImsServiceController = new IImsServiceController.Stub() { 133 @Override 134 public void setListener(IImsServiceControllerListener l) { 135 mListener = l; 136 } 137 138 @Override 139 public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) { 140 return createMmTelFeatureInternal(slotId, c); 141 } 142 143 @Override 144 public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) { 145 return createRcsFeatureInternal(slotId, c); 146 } 147 148 @Override 149 public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c) { 150 ImsService.this.removeImsFeature(slotId, featureType, c); 151 } 152 153 @Override 154 public ImsFeatureConfiguration querySupportedImsFeatures() { 155 return ImsService.this.querySupportedImsFeatures(); 156 } 157 158 @Override 159 public void notifyImsServiceReadyForFeatureCreation() { 160 ImsService.this.readyForFeatureCreation(); 161 } 162 163 @Override 164 public IImsConfig getConfig(int slotId) { 165 ImsConfigImplBase c = ImsService.this.getConfig(slotId); 166 return c != null ? c.getIImsConfig() : null; 167 } 168 169 @Override 170 public IImsRegistration getRegistration(int slotId) { 171 ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId); 172 return r != null ? r.getBinder() : null; 173 } 174 175 @Override 176 public void enableIms(int slotId) { 177 ImsService.this.enableIms(slotId); 178 } 179 180 @Override 181 public void disableIms(int slotId) { 182 ImsService.this.disableIms(slotId); 183 } 184 }; 185 186 /** 187 * @hide 188 */ 189 @Override 190 public IBinder onBind(Intent intent) { 191 if(SERVICE_INTERFACE.equals(intent.getAction())) { 192 Log.i(LOG_TAG, "ImsService Bound."); 193 return mImsServiceController; 194 } 195 return null; 196 } 197 198 /** 199 * @hide 200 */ 201 @VisibleForTesting 202 public SparseArray<ImsFeature> getFeatures(int slotId) { 203 return mFeaturesBySlot.get(slotId); 204 } 205 206 private IImsMmTelFeature createMmTelFeatureInternal(int slotId, 207 IImsFeatureStatusCallback c) { 208 MmTelFeature f = createMmTelFeature(slotId); 209 if (f != null) { 210 setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c); 211 return f.getBinder(); 212 } else { 213 Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned."); 214 return null; 215 } 216 } 217 218 private IImsRcsFeature createRcsFeatureInternal(int slotId, 219 IImsFeatureStatusCallback c) { 220 RcsFeature f = createRcsFeature(slotId); 221 if (f != null) { 222 setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c); 223 return f.getBinder(); 224 } else { 225 Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned."); 226 return null; 227 } 228 } 229 230 private void setupFeature(ImsFeature f, int slotId, int featureType, 231 IImsFeatureStatusCallback c) { 232 f.addImsFeatureStatusCallback(c); 233 f.initialize(this, slotId); 234 addImsFeature(slotId, featureType, f); 235 } 236 237 private void addImsFeature(int slotId, int featureType, ImsFeature f) { 238 synchronized (mFeaturesBySlot) { 239 // Get SparseArray for Features, by querying slot Id 240 SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); 241 if (features == null) { 242 // Populate new SparseArray of features if it doesn't exist for this slot yet. 243 features = new SparseArray<>(); 244 mFeaturesBySlot.put(slotId, features); 245 } 246 features.put(featureType, f); 247 } 248 } 249 250 private void removeImsFeature(int slotId, int featureType, 251 IImsFeatureStatusCallback c) { 252 synchronized (mFeaturesBySlot) { 253 // get ImsFeature associated with the slot/feature 254 SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); 255 if (features == null) { 256 Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot " 257 + slotId); 258 return; 259 } 260 ImsFeature f = features.get(featureType); 261 if (f == null) { 262 Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type " 263 + featureType + " exists on slot " + slotId); 264 return; 265 } 266 f.removeImsFeatureStatusCallback(c); 267 f.onFeatureRemoved(); 268 features.remove(featureType); 269 } 270 } 271 272 /** 273 * When called, provide the {@link ImsFeatureConfiguration} that this {@link ImsService} 274 * currently supports. This will trigger the framework to set up the {@link ImsFeature}s that 275 * correspond to the {@link ImsFeature}s configured here. 276 * 277 * Use {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} to change the supported 278 * {@link ImsFeature}s. 279 * 280 * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports. 281 */ 282 public ImsFeatureConfiguration querySupportedImsFeatures() { 283 // Return empty for base implementation 284 return new ImsFeatureConfiguration(); 285 } 286 287 /** 288 * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated 289 * features, that this {@link ImsService} supports. This may trigger the framework to add/remove 290 * new ImsFeatures, depending on the configuration. 291 */ 292 public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) 293 throws RemoteException { 294 if (mListener == null) { 295 throw new IllegalStateException("Framework is not ready"); 296 } 297 mListener.onUpdateSupportedImsFeatures(c); 298 } 299 300 /** 301 * The ImsService has been bound and is ready for ImsFeature creation based on the Features that 302 * the ImsService has registered for with the framework, either in the manifest or via 303 * {@link #querySupportedImsFeatures()}. 304 * 305 * The ImsService should use this signal instead of onCreate/onBind or similar to perform 306 * feature initialization because the framework may bind to this service multiple times to 307 * query the ImsService's {@link ImsFeatureConfiguration} via 308 * {@link #querySupportedImsFeatures()}before creating features. 309 */ 310 public void readyForFeatureCreation() { 311 } 312 313 /** 314 * The framework has enabled IMS for the slot specified, the ImsService should register for IMS 315 * and perform all appropriate initialization to bring up all ImsFeatures. 316 */ 317 public void enableIms(int slotId) { 318 } 319 320 /** 321 * The framework has disabled IMS for the slot specified. The ImsService must deregister for IMS 322 * and set capability status to false for all ImsFeatures. 323 */ 324 public void disableIms(int slotId) { 325 } 326 327 /** 328 * When called, the framework is requesting that a new {@link MmTelFeature} is created for the 329 * specified slot. 330 * 331 * @param slotId The slot ID that the MMTEL Feature is being created for. 332 * @return The newly created {@link MmTelFeature} associated with the slot or null if the 333 * feature is not supported. 334 */ 335 public MmTelFeature createMmTelFeature(int slotId) { 336 return null; 337 } 338 339 /** 340 * When called, the framework is requesting that a new {@link RcsFeature} is created for the 341 * specified slot. 342 * 343 * @param slotId The slot ID that the RCS Feature is being created for. 344 * @return The newly created {@link RcsFeature} associated with the slot or null if the feature 345 * is not supported. 346 */ 347 public RcsFeature createRcsFeature(int slotId) { 348 return null; 349 } 350 351 /** 352 * Return the {@link ImsConfigImplBase} implementation associated with the provided slot. This 353 * will be used by the platform to get/set specific IMS related configurations. 354 * 355 * @param slotId The slot that the IMS configuration is associated with. 356 * @return ImsConfig implementation that is associated with the specified slot. 357 */ 358 public ImsConfigImplBase getConfig(int slotId) { 359 return new ImsConfigImplBase(); 360 } 361 362 /** 363 * Return the {@link ImsRegistrationImplBase} implementation associated with the provided slot. 364 * 365 * @param slotId The slot that is associated with the IMS Registration. 366 * @return the ImsRegistration implementation associated with the slot. 367 */ 368 public ImsRegistrationImplBase getRegistration(int slotId) { 369 return new ImsRegistrationImplBase(); 370 } 371 }