1 /* 2 * Copyright (C) 2018 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.compat; 18 19 import android.annotation.Nullable; 20 import android.app.Service; 21 import android.compat.annotation.UnsupportedAppUsage; 22 import android.content.Intent; 23 import android.os.Build; 24 import android.os.IBinder; 25 import android.telephony.CarrierConfigManager; 26 import android.telephony.ims.compat.feature.ImsFeature; 27 import android.telephony.ims.compat.feature.MMTelFeature; 28 import android.telephony.ims.compat.feature.RcsFeature; 29 import android.util.Log; 30 import android.util.SparseArray; 31 32 import com.android.ims.internal.IImsFeatureStatusCallback; 33 import com.android.ims.internal.IImsMMTelFeature; 34 import com.android.ims.internal.IImsRcsFeature; 35 import com.android.ims.internal.IImsServiceController; 36 import com.android.internal.annotations.VisibleForTesting; 37 38 /** 39 * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend 40 * ImsService must register the service in their AndroidManifest to be detected by the framework. 41 * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE" 42 * permission. Then, the ImsService definition in the manifest must follow the following format: 43 * 44 * ... 45 * <service android:name=".EgImsService" 46 * android:permission="android.permission.BIND_IMS_SERVICE" > 47 * <!-- Apps must declare which features they support as metadata. The different categories are 48 * defined below. In this example, the RCS_FEATURE feature is supported. --> 49 * <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" /> 50 * <intent-filter> 51 * <action android:name="android.telephony.ims.compat.ImsService" /> 52 * </intent-filter> 53 * </service> 54 * ... 55 * 56 * The telephony framework will then bind to the ImsService you have defined in your manifest 57 * if you are either: 58 * 1) Defined as the default ImsService for the device in the device overlay using 59 * "config_ims_package". 60 * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using 61 * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}. 62 * 63 * The features that are currently supported in an ImsService are: 64 * - RCS_FEATURE: This ImsService implements the RcsFeature class. 65 * - MMTEL_FEATURE: This ImsService implements the MMTelFeature class. 66 * - EMERGENCY_MMTEL_FEATURE: This ImsService implements the MMTelFeature class and will be 67 * available to place emergency calls at all times. This MUST be implemented by the default 68 * ImsService provided in the device overlay. 69 * @hide 70 */ 71 public class ImsService extends Service { 72 73 private static final String LOG_TAG = "ImsService(Compat)"; 74 75 /** 76 * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService. 77 * @hide 78 */ 79 public static final String SERVICE_INTERFACE = "android.telephony.ims.compat.ImsService"; 80 81 // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that 82 // slot. 83 // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and 84 // call ImsFeature#onFeatureRemoved. 85 private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>(); 86 87 /** 88 * @hide 89 */ 90 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 91 protected final IBinder mImsServiceController = new IImsServiceController.Stub() { 92 93 @Override 94 public IImsMMTelFeature createEmergencyMMTelFeature(int slotId) { 95 return createEmergencyMMTelFeatureInternal(slotId); 96 } 97 98 @Override 99 public IImsMMTelFeature createMMTelFeature(int slotId) { 100 return createMMTelFeatureInternal(slotId); 101 } 102 103 @Override 104 public IImsRcsFeature createRcsFeature(int slotId) { 105 return createRcsFeatureInternal(slotId); 106 } 107 108 @Override 109 public void removeImsFeature(int slotId, int featureType) { 110 ImsService.this.removeImsFeature(slotId, featureType); 111 } 112 113 @Override 114 public void addFeatureStatusCallback(int slotId, int featureType, 115 IImsFeatureStatusCallback c) { 116 addImsFeatureStatusCallback(slotId, featureType, c); 117 } 118 119 @Override 120 public void removeFeatureStatusCallback(int slotId, int featureType, 121 IImsFeatureStatusCallback c) { 122 removeImsFeatureStatusCallback(slotId, featureType, c); 123 } 124 }; 125 126 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) ImsService()127 public ImsService() { 128 } 129 130 /** 131 * @hide 132 */ 133 @Override onBind(Intent intent)134 public IBinder onBind(Intent intent) { 135 if(SERVICE_INTERFACE.equals(intent.getAction())) { 136 Log.i(LOG_TAG, "ImsService(Compat) Bound."); 137 return mImsServiceController; 138 } 139 return null; 140 } 141 142 /** 143 * @hide 144 */ 145 @VisibleForTesting getFeatures(int slotId)146 public SparseArray<ImsFeature> getFeatures(int slotId) { 147 return mFeaturesBySlot.get(slotId); 148 } 149 createEmergencyMMTelFeatureInternal(int slotId)150 private IImsMMTelFeature createEmergencyMMTelFeatureInternal(int slotId) { 151 MMTelFeature f = onCreateEmergencyMMTelImsFeature(slotId); 152 if (f != null) { 153 setupFeature(f, slotId, ImsFeature.EMERGENCY_MMTEL); 154 return f.getBinder(); 155 } else { 156 return null; 157 } 158 } 159 createMMTelFeatureInternal(int slotId)160 private IImsMMTelFeature createMMTelFeatureInternal(int slotId) { 161 MMTelFeature f = onCreateMMTelImsFeature(slotId); 162 if (f != null) { 163 setupFeature(f, slotId, ImsFeature.MMTEL); 164 return f.getBinder(); 165 } else { 166 return null; 167 } 168 } 169 createRcsFeatureInternal(int slotId)170 private IImsRcsFeature createRcsFeatureInternal(int slotId) { 171 RcsFeature f = onCreateRcsFeature(slotId); 172 if (f != null) { 173 setupFeature(f, slotId, ImsFeature.RCS); 174 return f.getBinder(); 175 } else { 176 return null; 177 } 178 } 179 setupFeature(ImsFeature f, int slotId, int featureType)180 private void setupFeature(ImsFeature f, int slotId, int featureType) { 181 f.setContext(this); 182 f.setSlotId(slotId); 183 addImsFeature(slotId, featureType, f); 184 f.onFeatureReady(); 185 } 186 addImsFeature(int slotId, int featureType, ImsFeature f)187 private void addImsFeature(int slotId, int featureType, ImsFeature f) { 188 synchronized (mFeaturesBySlot) { 189 // Get SparseArray for Features, by querying slot Id 190 SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); 191 if (features == null) { 192 // Populate new SparseArray of features if it doesn't exist for this slot yet. 193 features = new SparseArray<>(); 194 mFeaturesBySlot.put(slotId, features); 195 } 196 features.put(featureType, f); 197 } 198 } 199 addImsFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c)200 private void addImsFeatureStatusCallback(int slotId, int featureType, 201 IImsFeatureStatusCallback c) { 202 synchronized (mFeaturesBySlot) { 203 // get ImsFeature associated with the slot/feature 204 SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); 205 if (features == null) { 206 Log.w(LOG_TAG, "Can not add ImsFeatureStatusCallback. No ImsFeatures exist on" 207 + " slot " + slotId); 208 return; 209 } 210 ImsFeature f = features.get(featureType); 211 if (f != null) { 212 f.addImsFeatureStatusCallback(c); 213 } 214 } 215 } 216 removeImsFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c)217 private void removeImsFeatureStatusCallback(int slotId, int featureType, 218 IImsFeatureStatusCallback c) { 219 synchronized (mFeaturesBySlot) { 220 // get ImsFeature associated with the slot/feature 221 SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); 222 if (features == null) { 223 Log.w(LOG_TAG, "Can not remove ImsFeatureStatusCallback. No ImsFeatures exist on" 224 + " slot " + slotId); 225 return; 226 } 227 ImsFeature f = features.get(featureType); 228 if (f != null) { 229 f.removeImsFeatureStatusCallback(c); 230 } 231 } 232 } 233 removeImsFeature(int slotId, int featureType)234 private void removeImsFeature(int slotId, int featureType) { 235 synchronized (mFeaturesBySlot) { 236 // get ImsFeature associated with the slot/feature 237 SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); 238 if (features == null) { 239 Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot " 240 + slotId); 241 return; 242 } 243 ImsFeature f = features.get(featureType); 244 if (f == null) { 245 Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type " 246 + featureType + " exists on slot " + slotId); 247 return; 248 } 249 f.onFeatureRemoved(); 250 features.remove(featureType); 251 } 252 } 253 254 /** 255 * @return An implementation of MMTelFeature that will be used by the system for MMTel 256 * functionality. Must be able to handle emergency calls at any time as well. 257 * @hide 258 */ onCreateEmergencyMMTelImsFeature(int slotId)259 public @Nullable MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) { 260 return null; 261 } 262 263 /** 264 * @return An implementation of MMTelFeature that will be used by the system for MMTel 265 * functionality. 266 * @hide 267 */ onCreateMMTelImsFeature(int slotId)268 public @Nullable MMTelFeature onCreateMMTelImsFeature(int slotId) { 269 return null; 270 } 271 272 /** 273 * @return An implementation of RcsFeature that will be used by the system for RCS. 274 * @hide 275 */ onCreateRcsFeature(int slotId)276 public @Nullable RcsFeature onCreateRcsFeature(int slotId) { 277 return null; 278 } 279 } 280