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