ImsService.java revision e0a7345c6a353411c73f7f45875478684472a91c
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 17package android.telephony.ims; 18 19import android.app.PendingIntent; 20import android.content.Intent; 21import android.content.pm.PackageManager; 22import android.os.IBinder; 23import android.os.Message; 24import android.os.RemoteException; 25import android.telephony.CarrierConfigManager; 26import android.telephony.ims.feature.ImsFeature; 27import android.telephony.ims.feature.MMTelFeature; 28import android.telephony.ims.feature.RcsFeature; 29import android.util.Log; 30import android.util.SparseArray; 31 32import com.android.ims.ImsCallProfile; 33import com.android.ims.internal.IImsCallSession; 34import com.android.ims.internal.IImsCallSessionListener; 35import com.android.ims.internal.IImsConfig; 36import com.android.ims.internal.IImsEcbm; 37import com.android.ims.internal.IImsFeatureStatusCallback; 38import com.android.ims.internal.IImsMultiEndpoint; 39import com.android.ims.internal.IImsRegistrationListener; 40import com.android.ims.internal.IImsServiceController; 41import com.android.ims.internal.IImsServiceFeatureListener; 42import com.android.ims.internal.IImsUt; 43import com.android.internal.annotations.VisibleForTesting; 44 45import static android.Manifest.permission.MODIFY_PHONE_STATE; 46import static android.Manifest.permission.READ_PHONE_STATE; 47import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE; 48 49/** 50 * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend 51 * ImsService must register the service in their AndroidManifest to be detected by the framework. 52 * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE" 53 * permission. Then, the ImsService definition in the manifest must follow the following format: 54 * 55 * ... 56 * <service android:name=".EgImsService" 57 * android:permission="android.permission.BIND_IMS_SERVICE" > 58 * <!-- Apps must declare which features they support as metadata. The different categories are 59 * defined below. In this example, the RCS_FEATURE feature is supported. --> 60 * <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" /> 61 * <intent-filter> 62 * <action android:name="android.telephony.ims.ImsService" /> 63 * </intent-filter> 64 * </service> 65 * ... 66 * 67 * The telephony framework will then bind to the ImsService you have defined in your manifest 68 * if you are either: 69 * 1) Defined as the default ImsService for the device in the device overlay using 70 * "config_ims_package". 71 * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using 72 * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}. 73 * 74 * The features that are currently supported in an ImsService are: 75 * - RCS_FEATURE: This ImsService implements the {@link RcsFeature} class. 76 * - MMTEL_FEATURE: This ImsService implements the {@link MMTelFeature} class. 77 * - EMERGENCY_MMTEL_FEATURE: This ImsService implements the {@link MMTelFeature} class and will be 78 * available to place emergency calls at all times. This MUST be implemented by the default 79 * ImsService provided in the device overlay. 80 * 81 * @hide 82 */ 83public abstract class ImsService extends ImsServiceBase { 84 85 private static final String LOG_TAG = "ImsService"; 86 87 /** 88 * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService. 89 */ 90 public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService"; 91 92 // A map of slot Id -> Set of features corresponding to that slot. 93 private final SparseArray<SparseArray<ImsFeature>> mFeatures = new SparseArray<>(); 94 95 // Implements all supported features as a flat interface. 96 protected final IBinder mImsServiceController = new IImsServiceController.Stub() { 97 98 @Override 99 public void createImsFeature(int slotId, int feature, IImsFeatureStatusCallback c) 100 throws RemoteException { 101 synchronized (mFeatures) { 102 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createImsFeature"); 103 onCreateImsFeatureInternal(slotId, feature, c); 104 } 105 } 106 107 @Override 108 public void removeImsFeature(int slotId, int feature) throws RemoteException { 109 synchronized (mFeatures) { 110 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "removeImsFeature"); 111 onRemoveImsFeatureInternal(slotId, feature); 112 } 113 } 114 115 @Override 116 public int startSession(int slotId, int featureType, PendingIntent incomingCallIntent, 117 IImsRegistrationListener listener) throws RemoteException { 118 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "startSession"); 119 synchronized (mFeatures) { 120 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 121 if (feature != null) { 122 return feature.startSession(incomingCallIntent, listener); 123 } 124 } 125 return 0; 126 } 127 128 @Override 129 public void endSession(int slotId, int featureType, int sessionId) throws RemoteException { 130 synchronized (mFeatures) { 131 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "endSession"); 132 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 133 if (feature != null) { 134 feature.endSession(sessionId); 135 } 136 } 137 } 138 139 @Override 140 public boolean isConnected(int slotId, int featureType, int callSessionType, int callType) 141 throws RemoteException { 142 enforceReadPhoneStatePermission("isConnected"); 143 synchronized (mFeatures) { 144 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 145 if (feature != null) { 146 return feature.isConnected(callSessionType, callType); 147 } 148 } 149 return false; 150 } 151 152 @Override 153 public boolean isOpened(int slotId, int featureType) throws RemoteException { 154 enforceReadPhoneStatePermission("isOpened"); 155 synchronized (mFeatures) { 156 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 157 if (feature != null) { 158 return feature.isOpened(); 159 } 160 } 161 return false; 162 } 163 164 @Override 165 public int getFeatureStatus(int slotId, int featureType) throws RemoteException { 166 enforceReadPhoneStatePermission("getFeatureStatus"); 167 int status = ImsFeature.STATE_NOT_AVAILABLE; 168 synchronized (mFeatures) { 169 SparseArray<ImsFeature> featureMap = mFeatures.get(slotId); 170 if (featureMap != null) { 171 ImsFeature feature = getImsFeatureFromType(featureMap, featureType); 172 if (feature != null) { 173 status = feature.getFeatureState(); 174 } 175 } 176 } 177 return status; 178 } 179 180 @Override 181 public void addRegistrationListener(int slotId, int featureType, 182 IImsRegistrationListener listener) throws RemoteException { 183 enforceReadPhoneStatePermission("addRegistrationListener"); 184 synchronized (mFeatures) { 185 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 186 if (feature != null) { 187 feature.addRegistrationListener(listener); 188 } 189 } 190 } 191 192 @Override 193 public void removeRegistrationListener(int slotId, int featureType, 194 IImsRegistrationListener listener) throws RemoteException { 195 enforceReadPhoneStatePermission("removeRegistrationListener"); 196 synchronized (mFeatures) { 197 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 198 if (feature != null) { 199 feature.removeRegistrationListener(listener); 200 } 201 } 202 } 203 204 @Override 205 public ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId, 206 int callSessionType, int callType) throws RemoteException { 207 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createCallProfile"); 208 synchronized (mFeatures) { 209 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 210 if (feature != null) { 211 return feature.createCallProfile(sessionId, callSessionType, callType); 212 } 213 } 214 return null; 215 } 216 217 @Override 218 public IImsCallSession createCallSession(int slotId, int featureType, int sessionId, 219 ImsCallProfile profile, IImsCallSessionListener listener) throws RemoteException { 220 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createCallSession"); 221 synchronized (mFeatures) { 222 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 223 if (feature != null) { 224 return feature.createCallSession(sessionId, profile, listener); 225 } 226 } 227 return null; 228 } 229 230 @Override 231 public IImsCallSession getPendingCallSession(int slotId, int featureType, int sessionId, 232 String callId) throws RemoteException { 233 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getPendingCallSession"); 234 synchronized (mFeatures) { 235 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 236 if (feature != null) { 237 return feature.getPendingCallSession(sessionId, callId); 238 } 239 } 240 return null; 241 } 242 243 @Override 244 public IImsUt getUtInterface(int slotId, int featureType) 245 throws RemoteException { 246 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getUtInterface"); 247 synchronized (mFeatures) { 248 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 249 if (feature != null) { 250 return feature.getUtInterface(); 251 } 252 } 253 return null; 254 } 255 256 @Override 257 public IImsConfig getConfigInterface(int slotId, int featureType) 258 throws RemoteException { 259 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getConfigInterface"); 260 synchronized (mFeatures) { 261 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 262 if (feature != null) { 263 return feature.getConfigInterface(); 264 } 265 } 266 return null; 267 } 268 269 @Override 270 public void turnOnIms(int slotId, int featureType) throws RemoteException { 271 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "turnOnIms"); 272 synchronized (mFeatures) { 273 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 274 if (feature != null) { 275 feature.turnOnIms(); 276 } 277 } 278 } 279 280 @Override 281 public void turnOffIms(int slotId, int featureType) throws RemoteException { 282 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "turnOffIms"); 283 synchronized (mFeatures) { 284 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 285 if (feature != null) { 286 feature.turnOffIms(); 287 } 288 } 289 } 290 291 @Override 292 public IImsEcbm getEcbmInterface(int slotId, int featureType) 293 throws RemoteException { 294 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getEcbmInterface"); 295 synchronized (mFeatures) { 296 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 297 if (feature != null) { 298 return feature.getEcbmInterface(); 299 } 300 } 301 return null; 302 } 303 304 @Override 305 public void setUiTTYMode(int slotId, int featureType, int uiTtyMode, Message onComplete) 306 throws RemoteException { 307 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "setUiTTYMode"); 308 synchronized (mFeatures) { 309 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 310 if (feature != null) { 311 feature.setUiTTYMode(uiTtyMode, onComplete); 312 } 313 } 314 } 315 316 @Override 317 public IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType) 318 throws RemoteException { 319 enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getMultiEndpointInterface"); 320 synchronized (mFeatures) { 321 MMTelFeature feature = resolveMMTelFeature(slotId, featureType); 322 if (feature != null) { 323 return feature.getMultiEndpointInterface(); 324 } 325 } 326 return null; 327 } 328 329 }; 330 331 @Override 332 public IBinder onBind(Intent intent) { 333 if(SERVICE_INTERFACE.equals(intent.getAction())) { 334 return mImsServiceController; 335 } 336 return null; 337 } 338 339 /** 340 * Called from the ImsResolver to create the requested ImsFeature, as defined by the slot and 341 * featureType 342 * @param slotId An integer representing which SIM slot the ImsFeature is assigned to. 343 * @param featureType An integer representing the type of ImsFeature being created. This is 344 * defined in {@link ImsFeature}. 345 */ 346 // Be sure to lock on mFeatures before accessing this method 347 private void onCreateImsFeatureInternal(int slotId, int featureType, 348 IImsFeatureStatusCallback c) { 349 SparseArray<ImsFeature> featureMap = mFeatures.get(slotId); 350 if (featureMap == null) { 351 featureMap = new SparseArray<>(); 352 mFeatures.put(slotId, featureMap); 353 } 354 ImsFeature f = makeImsFeature(slotId, featureType); 355 if (f != null) { 356 f.setContext(this); 357 f.setSlotId(slotId); 358 f.setImsFeatureStatusCallback(c); 359 featureMap.put(featureType, f); 360 } 361 362 } 363 /** 364 * Called from the ImsResolver to remove an existing ImsFeature, as defined by the slot and 365 * featureType. 366 * @param slotId An integer representing which SIM slot the ImsFeature is assigned to. 367 * @param featureType An integer representing the type of ImsFeature being removed. This is 368 * defined in {@link ImsFeature}. 369 */ 370 // Be sure to lock on mFeatures before accessing this method 371 private void onRemoveImsFeatureInternal(int slotId, int featureType) { 372 SparseArray<ImsFeature> featureMap = mFeatures.get(slotId); 373 if (featureMap == null) { 374 return; 375 } 376 377 ImsFeature featureToRemove = getImsFeatureFromType(featureMap, featureType); 378 if (featureToRemove != null) { 379 featureMap.remove(featureType); 380 featureToRemove.notifyFeatureRemoved(slotId); 381 // Remove reference to Binder 382 featureToRemove.setImsFeatureStatusCallback(null); 383 } 384 } 385 386 // Be sure to lock on mFeatures before accessing this method 387 private MMTelFeature resolveMMTelFeature(int slotId, int featureType) { 388 SparseArray<ImsFeature> features = getImsFeatureMap(slotId); 389 MMTelFeature feature = null; 390 if (features != null) { 391 feature = resolveImsFeature(features, featureType, MMTelFeature.class); 392 } 393 return feature; 394 } 395 396 // Be sure to lock on mFeatures before accessing this method 397 private <T extends ImsFeature> T resolveImsFeature(SparseArray<ImsFeature> set, int featureType, 398 Class<T> className) { 399 ImsFeature feature = getImsFeatureFromType(set, featureType); 400 if (feature == null) { 401 return null; 402 } 403 try { 404 return className.cast(feature); 405 } catch (ClassCastException e) 406 { 407 Log.e(LOG_TAG, "Can not cast ImsFeature! Exception: " + e.getMessage()); 408 } 409 return null; 410 } 411 412 @VisibleForTesting 413 // Be sure to lock on mFeatures before accessing this method 414 public SparseArray<ImsFeature> getImsFeatureMap(int slotId) { 415 return mFeatures.get(slotId); 416 } 417 418 @VisibleForTesting 419 // Be sure to lock on mFeatures before accessing this method 420 public ImsFeature getImsFeatureFromType(SparseArray<ImsFeature> set, int featureType) { 421 return set.get(featureType); 422 } 423 424 private ImsFeature makeImsFeature(int slotId, int feature) { 425 switch (feature) { 426 case ImsFeature.EMERGENCY_MMTEL: { 427 return onCreateEmergencyMMTelImsFeature(slotId); 428 } 429 case ImsFeature.MMTEL: { 430 return onCreateMMTelImsFeature(slotId); 431 } 432 case ImsFeature.RCS: { 433 return onCreateRcsFeature(slotId); 434 } 435 } 436 // Tried to create feature that is not defined. 437 return null; 438 } 439 440 /** 441 * Check for both READ_PHONE_STATE and READ_PRIVILEGED_PHONE_STATE. READ_PHONE_STATE is a 442 * public permission and READ_PRIVILEGED_PHONE_STATE is only granted to system apps. 443 */ 444 private void enforceReadPhoneStatePermission(String fn) { 445 if (checkCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE) 446 != PackageManager.PERMISSION_GRANTED) { 447 enforceCallingOrSelfPermission(READ_PHONE_STATE, fn); 448 } 449 } 450 451 /** 452 * @return An implementation of MMTelFeature that will be used by the system for MMTel 453 * functionality. Must be able to handle emergency calls at any time as well. 454 */ 455 public abstract MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId); 456 457 /** 458 * @return An implementation of MMTelFeature that will be used by the system for MMTel 459 * functionality. 460 */ 461 public abstract MMTelFeature onCreateMMTelImsFeature(int slotId); 462 463 /** 464 * @return An implementation of RcsFeature that will be used by the system for RCS. 465 */ 466 public abstract RcsFeature onCreateRcsFeature(int slotId); 467} 468