• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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