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