• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.telephony.ims;
18 
19 import android.annotation.LongDef;
20 import android.annotation.Nullable;
21 import android.annotation.SuppressLint;
22 import android.annotation.SystemApi;
23 import android.app.Service;
24 import android.content.Intent;
25 import android.os.IBinder;
26 import android.os.RemoteException;
27 import android.telephony.CarrierConfigManager;
28 import android.telephony.ims.aidl.IImsConfig;
29 import android.telephony.ims.aidl.IImsMmTelFeature;
30 import android.telephony.ims.aidl.IImsRcsFeature;
31 import android.telephony.ims.aidl.IImsRegistration;
32 import android.telephony.ims.aidl.IImsServiceController;
33 import android.telephony.ims.aidl.IImsServiceControllerListener;
34 import android.telephony.ims.aidl.ISipTransport;
35 import android.telephony.ims.feature.ImsFeature;
36 import android.telephony.ims.feature.MmTelFeature;
37 import android.telephony.ims.feature.RcsFeature;
38 import android.telephony.ims.stub.ImsConfigImplBase;
39 import android.telephony.ims.stub.ImsFeatureConfiguration;
40 import android.telephony.ims.stub.ImsRegistrationImplBase;
41 import android.telephony.ims.stub.SipTransportImplBase;
42 import android.util.Log;
43 import android.util.SparseArray;
44 
45 import com.android.ims.internal.IImsFeatureStatusCallback;
46 import com.android.internal.annotations.VisibleForTesting;
47 
48 import java.lang.annotation.Retention;
49 import java.lang.annotation.RetentionPolicy;
50 import java.util.HashMap;
51 import java.util.Map;
52 
53 /**
54  * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
55  * ImsService must register the service in their AndroidManifest to be detected by the framework.
56  * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
57  * permission. Then, the ImsService definition in the manifest must follow the following format:
58  *
59  * ...
60  * <service android:name=".EgImsService"
61  *     android:permission="android.permission.BIND_IMS_SERVICE" >
62  *     ...
63  *     <intent-filter>
64  *         <action android:name="android.telephony.ims.ImsService" />
65  *     </intent-filter>
66  * </service>
67  * ...
68  *
69  * The telephony framework will then bind to the ImsService you have defined in your manifest
70  * if you are either:
71  * 1) Defined as the default ImsService for the device in the device overlay using
72  *    "config_ims_mmtel_package" or "config_ims_rcs_package".
73  * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
74  *    {@link CarrierConfigManager#KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING} or
75  *    {@link CarrierConfigManager#KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING}.
76  *
77  * There are two ways to define to the platform which {@link ImsFeature}s this {@link ImsService}
78  * supports, dynamic or static definitions.
79  *
80  * In the static definition, the {@link ImsFeature}s that are supported are defined in the service
81  * definition of the AndroidManifest.xml file as metadata:
82  * <!-- Apps must declare which features they support as metadata. The different categories are
83  *      defined below. In this example, the MMTEL_FEATURE feature is supported. -->
84  * <meta-data android:name="android.telephony.ims.MMTEL_FEATURE" android:value="true" />
85  *
86  * The features that are currently supported in an ImsService are:
87  * - RCS_FEATURE: This ImsService implements the RcsFeature class.
88  * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class.
89  * - EMERGENCY_MMTEL_FEATURE: This ImsService supports Emergency Calling for MMTEL, must be
90  *   declared along with the MMTEL_FEATURE. If this is not specified, the framework will use
91  *   circuit switch for emergency calling.
92  *
93  * In the dynamic definition, the supported features are not specified in the service definition
94  * of the AndroidManifest. Instead, the framework binds to this service and calls
95  * {@link #querySupportedImsFeatures()}. The {@link ImsService} then returns an
96  * {@link ImsFeatureConfiguration}, which the framework uses to initialize the supported
97  * {@link ImsFeature}s. If at any time, the list of supported {@link ImsFeature}s changes,
98  * {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} can be called to tell the
99  * framework of the changes.
100  *
101  * @hide
102  */
103 @SystemApi
104 public class ImsService extends Service {
105 
106     private static final String LOG_TAG = "ImsService";
107 
108     /**
109      * This ImsService supports the capability to place emergency calls over MMTEL.
110      * <p>
111      * Note: This should never be set by {@link #getImsServiceCapabilities()}, as whether it is
112      * there or not depends on whether or not {@link ImsFeature#FEATURE_EMERGENCY_MMTEL} is defined
113      * for this ImsService. If it is set, it will be removed during sanitization before the final
114      * capabilities bitfield is sent back to the framework.
115      * @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be
116      * adding other capabilities in a central location, so track this capability here as well.
117      */
118     public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0;
119 
120     /**
121      * This ImsService supports the capability to create SIP delegates for other IMS applications
122      * to use to proxy SIP messaging traffic through it.
123      * <p>
124      * In order for the framework to report SipDelegate creation as being available for this
125      * ImsService implementation, this ImsService must report this capability flag in
126      * {@link #getImsServiceCapabilities()}, {@link #getSipTransport(int)} must not return null, and
127      * this ImsService MUST report the ability to create both {@link ImsFeature#FEATURE_MMTEL} and
128      * {@link ImsFeature#FEATURE_RCS} features.
129      */
130     public static final long CAPABILITY_SIP_DELEGATE_CREATION = 1 << 1;
131 
132     /**
133      * Used for internal correctness checks of capabilities set by the ImsService implementation and
134      * tracks the index of the largest defined flag in the capabilities long.
135      * @hide
136      */
137     public static final long CAPABILITY_MAX_INDEX =
138             Long.numberOfTrailingZeros(CAPABILITY_SIP_DELEGATE_CREATION);
139 
140     /**
141      * @hide
142      */
143     @LongDef(flag = true,
144             prefix = "CAPABILITY_",
145             value = {
146                     // CAPABILITY_EMERGENCY_OVER_MMTEL is not included here because it is managed by
147                     // whether or not ImsFeature.FEATURE_EMERGENCY_MMTEL feature is set and should
148                     // not be set by users of ImsService.
149                     CAPABILITY_SIP_DELEGATE_CREATION
150             })
151     @Retention(RetentionPolicy.SOURCE)
152     public @interface ImsServiceCapability {}
153 
154     /**
155      * Used for logging purposes, see {@link #getCapabilitiesString(long)}
156      * @hide
157      */
158     private static final Map<Long, String> CAPABILITIES_LOG_MAP = new HashMap<Long, String>() {{
159             put(CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL");
160             put(CAPABILITY_SIP_DELEGATE_CREATION, "SIP_DELEGATE_CREATION");
161         }};
162 
163     /**
164      * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
165      * @hide
166      */
167     public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
168 
169     // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
170     // slot.
171     // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
172     // call ImsFeature#onFeatureRemoved.
173     private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
174 
175     private IImsServiceControllerListener mListener;
176 
177 
178     /**
179      * Listener that notifies the framework of ImsService changes.
180      * @hide
181      */
182     public static class Listener extends IImsServiceControllerListener.Stub {
183         /**
184          * The IMS features that this ImsService supports has changed.
185          * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s
186          *   that this ImsService supports. This may trigger the addition/removal of feature
187          *   in this service.
188          */
onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)189         public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
190         }
191     }
192 
193     /**
194      * @hide
195      */
196     protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
197         @Override
198         public void setListener(IImsServiceControllerListener l) {
199             mListener = l;
200         }
201 
202         @Override
203         public IImsMmTelFeature createMmTelFeature(int slotId) {
204             return createMmTelFeatureInternal(slotId);
205         }
206 
207         @Override
208         public IImsRcsFeature createRcsFeature(int slotId) {
209             return createRcsFeatureInternal(slotId);
210         }
211 
212         @Override
213         public void addFeatureStatusCallback(int slotId, int featureType,
214                 IImsFeatureStatusCallback c) {
215             ImsService.this.addImsFeatureStatusCallback(slotId, featureType, c);
216         }
217 
218         @Override
219         public void removeFeatureStatusCallback(int slotId, int featureType,
220                 IImsFeatureStatusCallback c) {
221             ImsService.this.removeImsFeatureStatusCallback(slotId, featureType, c);
222         }
223 
224         @Override
225         public void removeImsFeature(int slotId, int featureType) {
226             ImsService.this.removeImsFeature(slotId, featureType);
227         }
228 
229         @Override
230         public ImsFeatureConfiguration querySupportedImsFeatures() {
231             return ImsService.this.querySupportedImsFeatures();
232         }
233 
234         @Override
235         public long getImsServiceCapabilities() {
236             long caps = ImsService.this.getImsServiceCapabilities();
237             long sanitizedCaps = sanitizeCapabilities(caps);
238             if (caps != sanitizedCaps) {
239                 Log.w(LOG_TAG, "removing invalid bits from field: 0x"
240                         + Long.toHexString(caps ^ sanitizedCaps));
241             }
242             return sanitizedCaps;
243         }
244 
245         @Override
246         public void notifyImsServiceReadyForFeatureCreation() {
247             ImsService.this.readyForFeatureCreation();
248         }
249 
250         @Override
251         public IImsConfig getConfig(int slotId) {
252             ImsConfigImplBase c = ImsService.this.getConfig(slotId);
253             return c != null ? c.getIImsConfig() : null;
254         }
255 
256         @Override
257         public IImsRegistration getRegistration(int slotId) {
258             ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
259             return r != null ? r.getBinder() : null;
260         }
261 
262         @Override
263         public ISipTransport getSipTransport(int slotId) {
264             SipTransportImplBase s = ImsService.this.getSipTransport(slotId);
265             return s != null ? s.getBinder() : null;
266         }
267 
268         @Override
269         public void enableIms(int slotId) {
270             ImsService.this.enableIms(slotId);
271         }
272 
273         @Override
274         public void disableIms(int slotId) {
275             ImsService.this.disableIms(slotId);
276         }
277     };
278 
279     /**
280      * @hide
281      */
282     @Override
onBind(Intent intent)283     public IBinder onBind(Intent intent) {
284         if(SERVICE_INTERFACE.equals(intent.getAction())) {
285             Log.i(LOG_TAG, "ImsService Bound.");
286             return mImsServiceController;
287         }
288         return null;
289     }
290 
291     /**
292      * @hide
293      */
294     @VisibleForTesting
getFeatures(int slotId)295     public SparseArray<ImsFeature> getFeatures(int slotId) {
296         return mFeaturesBySlot.get(slotId);
297     }
298 
createMmTelFeatureInternal(int slotId)299     private IImsMmTelFeature createMmTelFeatureInternal(int slotId) {
300         MmTelFeature f = createMmTelFeature(slotId);
301         if (f != null) {
302             setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
303             return f.getBinder();
304         } else {
305             Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
306             return null;
307         }
308     }
309 
createRcsFeatureInternal(int slotId)310     private IImsRcsFeature createRcsFeatureInternal(int slotId) {
311         RcsFeature f = createRcsFeature(slotId);
312         if (f != null) {
313             setupFeature(f, slotId, ImsFeature.FEATURE_RCS);
314             return f.getBinder();
315         } else {
316             Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
317             return null;
318         }
319     }
320 
setupFeature(ImsFeature f, int slotId, int featureType)321     private void setupFeature(ImsFeature f, int slotId, int featureType) {
322         f.initialize(this, slotId);
323         addImsFeature(slotId, featureType, f);
324     }
325 
addImsFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c)326     private void addImsFeatureStatusCallback(int slotId, int featureType,
327             IImsFeatureStatusCallback c) {
328         synchronized (mFeaturesBySlot) {
329             // get ImsFeature associated with the slot/feature
330             SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
331             if (features == null) {
332                 Log.w(LOG_TAG, "Can not add ImsFeatureStatusCallback - no features on slot "
333                         + slotId);
334                 return;
335             }
336             ImsFeature f = features.get(featureType);
337             if (f != null) {
338                 f.addImsFeatureStatusCallback(c);
339             }
340         }
341     }
342 
removeImsFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c)343     private void removeImsFeatureStatusCallback(int slotId, int featureType,
344             IImsFeatureStatusCallback c) {
345         synchronized (mFeaturesBySlot) {
346             // get ImsFeature associated with the slot/feature
347             SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
348             if (features == null) {
349                 Log.w(LOG_TAG, "Can not remove ImsFeatureStatusCallback - no features on slot "
350                         + slotId);
351                 return;
352             }
353             ImsFeature f = features.get(featureType);
354             if (f != null) {
355                 f.removeImsFeatureStatusCallback(c);
356             }
357         }
358     }
359 
addImsFeature(int slotId, int featureType, ImsFeature f)360     private void addImsFeature(int slotId, int featureType, ImsFeature f) {
361         synchronized (mFeaturesBySlot) {
362             // Get SparseArray for Features, by querying slot Id
363             SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
364             if (features == null) {
365                 // Populate new SparseArray of features if it doesn't exist for this slot yet.
366                 features = new SparseArray<>();
367                 mFeaturesBySlot.put(slotId, features);
368             }
369             features.put(featureType, f);
370         }
371     }
372 
removeImsFeature(int slotId, int featureType)373     private void removeImsFeature(int slotId, int featureType) {
374         synchronized (mFeaturesBySlot) {
375             // get ImsFeature associated with the slot/feature
376             SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
377             if (features == null) {
378                 Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
379                         + slotId);
380                 return;
381             }
382             ImsFeature f = features.get(featureType);
383             if (f == null) {
384                 Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
385                         + featureType + " exists on slot " + slotId);
386                 return;
387             }
388             f.onFeatureRemoved();
389             features.remove(featureType);
390         }
391     }
392 
393     /**
394      * When called, provide the {@link ImsFeatureConfiguration} that this {@link ImsService}
395      * currently supports. This will trigger the framework to set up the {@link ImsFeature}s that
396      * correspond to the {@link ImsFeature}s configured here.
397      *
398      * Use {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} to change the supported
399      * {@link ImsFeature}s.
400      *
401      * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports.
402      */
querySupportedImsFeatures()403     public ImsFeatureConfiguration querySupportedImsFeatures() {
404         // Return empty for base implementation
405         return new ImsFeatureConfiguration();
406     }
407 
408     /**
409      * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated
410      * features, that this {@link ImsService} supports. This may trigger the framework to add/remove
411      * new ImsFeatures, depending on the configuration.
412      */
onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)413     public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
414             throws RemoteException {
415         if (mListener == null) {
416             throw new IllegalStateException("Framework is not ready");
417         }
418         mListener.onUpdateSupportedImsFeatures(c);
419     }
420 
421     /**
422      * The optional capabilities that this ImsService supports.
423      * <p>
424      * This should be a static configuration and should not change at runtime.
425      * @return The optional static capabilities of this ImsService implementation.
426      */
427     // ImsService follows a different convention, since it is a stub class. The on* methods are
428     // final and call back into the framework with a state update.
429     @SuppressLint("OnNameExpected")
getImsServiceCapabilities()430     public @ImsServiceCapability long getImsServiceCapabilities() {
431         // Stub implementation to be implemented by ImsService.
432         return 0L;
433     }
434 
435     /**
436      * The ImsService has been bound and is ready for ImsFeature creation based on the Features that
437      * the ImsService has registered for with the framework, either in the manifest or via
438      * {@link #querySupportedImsFeatures()}.
439      *
440      * The ImsService should use this signal instead of onCreate/onBind or similar to perform
441      * feature initialization because the framework may bind to this service multiple times to
442      * query the ImsService's {@link ImsFeatureConfiguration} via
443      * {@link #querySupportedImsFeatures()}before creating features.
444      */
readyForFeatureCreation()445     public void readyForFeatureCreation() {
446     }
447 
448     /**
449      * The framework has enabled IMS for the slot specified, the ImsService should register for IMS
450      * and perform all appropriate initialization to bring up all ImsFeatures.
451      */
enableIms(int slotId)452     public void enableIms(int slotId) {
453     }
454 
455     /**
456      * The framework has disabled IMS for the slot specified. The ImsService must deregister for IMS
457      * and set capability status to false for all ImsFeatures.
458      */
disableIms(int slotId)459     public void disableIms(int slotId) {
460     }
461 
462     /**
463      * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
464      * specified slot.
465      *
466      * @param slotId The slot ID that the MMTEL Feature is being created for.
467      * @return The newly created {@link MmTelFeature} associated with the slot or null if the
468      * feature is not supported.
469      */
createMmTelFeature(int slotId)470     public MmTelFeature createMmTelFeature(int slotId) {
471         return null;
472     }
473 
474     /**
475      * When called, the framework is requesting that a new {@link RcsFeature} is created for the
476      * specified slot.
477      *
478      * @param slotId The slot ID that the RCS Feature is being created for.
479      * @return The newly created {@link RcsFeature} associated with the slot or null if the feature
480      * is not supported.
481      */
createRcsFeature(int slotId)482     public RcsFeature createRcsFeature(int slotId) {
483         return null;
484     }
485 
486     /**
487      * Return the {@link ImsConfigImplBase} implementation associated with the provided slot. This
488      * will be used by the platform to get/set specific IMS related configurations.
489      *
490      * @param slotId The slot that the IMS configuration is associated with.
491      * @return ImsConfig implementation that is associated with the specified slot.
492      */
getConfig(int slotId)493     public ImsConfigImplBase getConfig(int slotId) {
494         return new ImsConfigImplBase();
495     }
496 
497     /**
498      * Return the {@link ImsRegistrationImplBase} implementation associated with the provided slot.
499      *
500      * @param slotId The slot that is associated with the IMS Registration.
501      * @return the ImsRegistration implementation associated with the slot.
502      */
getRegistration(int slotId)503     public ImsRegistrationImplBase getRegistration(int slotId) {
504         return new ImsRegistrationImplBase();
505     }
506 
507     /**
508      * Return the {@link SipTransportImplBase} implementation associated with the provided slot.
509      * <p>
510      * This is an optional interface used for devices that must support IMS single registration and
511      * proxy SIP traffic to remote IMS applications. If this is not supported for this IMS service,
512      * this method should return {@code null}. If this feature is supported, then this method must
513      * never be {@code null} and the optional ImsService capability flag
514      * {@link #CAPABILITY_SIP_DELEGATE_CREATION} must be set in
515      * {@link #getImsServiceCapabilities()}. Otherwise the framework will assume this feature is not
516      * supported for this ImsService.
517      * @param slotId The slot that is associated with the SipTransport implementation.
518      * @return the SipTransport implementation for the specified slot.
519      */
520     // ImsService follows a different convention, since it is a stub class. The on* methods are
521     // final and call back into the framework with a state update.
522     @SuppressLint("OnNameExpected")
getSipTransport(int slotId)523     public @Nullable SipTransportImplBase getSipTransport(int slotId) {
524         // Stub implementation for ImsServices that do not support SipTransport.
525         return null;
526     }
527 
sanitizeCapabilities(@msServiceCapability long caps)528     private static long sanitizeCapabilities(@ImsServiceCapability long caps) {
529         long filter = 0xFFFFFFFFFFFFFFFFL;
530         // pad the "allowed" set with zeros
531         filter <<= CAPABILITY_MAX_INDEX + 1;
532         // remove values above the allowed set.
533         caps &= ~filter;
534         // CAPABILITY_EMERGENCY_OVER_MMTEL should also not be set here, will be set by telephony
535         // internally.
536         caps &= ~CAPABILITY_EMERGENCY_OVER_MMTEL;
537         return caps;
538     }
539 
540     /**
541      * @return A string representation of the ImsService capabilities for logging.
542      * @hide
543      */
getCapabilitiesString(@msServiceCapability long caps)544     public static String getCapabilitiesString(@ImsServiceCapability long caps) {
545         StringBuffer result = new StringBuffer();
546         result.append("capabilities={ ");
547         // filter incrementally fills 0s from  left to right. This is used to keep filtering out
548         // more bits in the long until the remaining leftmost bits are all zero.
549         long filter = 0xFFFFFFFFFFFFFFFFL;
550         // position of iterator to potentially print capability.
551         long i = 0;
552         while ((caps & filter) != 0 && i <= 63) {
553             long bitToCheck = (1L << i);
554             if ((caps & bitToCheck) != 0) {
555                 result.append(CAPABILITIES_LOG_MAP.getOrDefault(bitToCheck, bitToCheck + "?"));
556                 result.append(" ");
557             }
558             // shift left by one and fill in another 1 on the leftmost bit.
559             filter <<= 1;
560             i++;
561         }
562         result.append("}");
563         return result.toString();
564     }
565 }
566