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