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