• 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.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SuppressLint;
23 import android.annotation.SystemApi;
24 import android.app.Service;
25 import android.content.Intent;
26 import android.os.IBinder;
27 import android.os.RemoteException;
28 import android.telephony.CarrierConfigManager;
29 import android.telephony.ims.aidl.IImsConfig;
30 import android.telephony.ims.aidl.IImsMmTelFeature;
31 import android.telephony.ims.aidl.IImsRcsFeature;
32 import android.telephony.ims.aidl.IImsRegistration;
33 import android.telephony.ims.aidl.IImsServiceController;
34 import android.telephony.ims.aidl.IImsServiceControllerListener;
35 import android.telephony.ims.aidl.ISipTransport;
36 import android.telephony.ims.feature.ImsFeature;
37 import android.telephony.ims.feature.MmTelFeature;
38 import android.telephony.ims.feature.RcsFeature;
39 import android.telephony.ims.stub.ImsConfigImplBase;
40 import android.telephony.ims.stub.ImsFeatureConfiguration;
41 import android.telephony.ims.stub.ImsRegistrationImplBase;
42 import android.telephony.ims.stub.SipTransportImplBase;
43 import android.util.Log;
44 import android.util.SparseArray;
45 import android.util.SparseBooleanArray;
46 
47 import com.android.ims.internal.IImsFeatureStatusCallback;
48 import com.android.internal.annotations.VisibleForTesting;
49 import com.android.internal.telephony.util.TelephonyUtils;
50 
51 import java.lang.annotation.Retention;
52 import java.lang.annotation.RetentionPolicy;
53 import java.util.HashMap;
54 import java.util.Map;
55 import java.util.concurrent.CancellationException;
56 import java.util.concurrent.CompletableFuture;
57 import java.util.concurrent.CompletionException;
58 import java.util.concurrent.ExecutionException;
59 import java.util.concurrent.Executor;
60 import java.util.function.Supplier;
61 
62 /**
63  * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
64  * ImsService must register the service in their AndroidManifest to be detected by the framework.
65  * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
66  * permission. Then, the ImsService definition in the manifest must follow the following format:
67  *
68  * ...
69  * <service android:name=".EgImsService"
70  *     android:permission="android.permission.BIND_IMS_SERVICE" >
71  *     ...
72  *     <intent-filter>
73  *         <action android:name="android.telephony.ims.ImsService" />
74  *     </intent-filter>
75  * </service>
76  * ...
77  *
78  * The telephony framework will then bind to the ImsService you have defined in your manifest
79  * if you are either:
80  * 1) Defined as the default ImsService for the device in the device overlay using
81  *    "config_ims_mmtel_package" or "config_ims_rcs_package".
82  * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
83  *    {@link CarrierConfigManager#KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING} or
84  *    {@link CarrierConfigManager#KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING}.
85  *
86  * There are two ways to define to the platform which {@link ImsFeature}s this {@link ImsService}
87  * supports, dynamic or static definitions.
88  *
89  * In the static definition, the {@link ImsFeature}s that are supported are defined in the service
90  * definition of the AndroidManifest.xml file as metadata:
91  * <!-- Apps must declare which features they support as metadata. The different categories are
92  *      defined below. In this example, the MMTEL_FEATURE feature is supported. -->
93  * <meta-data android:name="android.telephony.ims.MMTEL_FEATURE" android:value="true" />
94  *
95  * The features that are currently supported in an ImsService are:
96  * - RCS_FEATURE: This ImsService implements the RcsFeature class.
97  * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class.
98  * - EMERGENCY_MMTEL_FEATURE: This ImsService supports Emergency Calling for MMTEL, must be
99  *   declared along with the MMTEL_FEATURE. If this is not specified, the framework will use
100  *   circuit switch for emergency calling.
101  *
102  * In the dynamic definition, the supported features are not specified in the service definition
103  * of the AndroidManifest. Instead, the framework binds to this service and calls
104  * {@link #querySupportedImsFeatures()}. The {@link ImsService} then returns an
105  * {@link ImsFeatureConfiguration}, which the framework uses to initialize the supported
106  * {@link ImsFeature}s. If at any time, the list of supported {@link ImsFeature}s changes,
107  * {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} can be called to tell the
108  * framework of the changes.
109  *
110  * @hide
111  */
112 @SystemApi
113 public class ImsService extends Service {
114 
115     private static final String LOG_TAG = "ImsService";
116 
117     /**
118      * This ImsService supports the capability to place emergency calls over MMTEL.
119      * <p>
120      * Note: This should never be set by {@link #getImsServiceCapabilities()}, as whether it is
121      * there or not depends on whether or not {@link ImsFeature#FEATURE_EMERGENCY_MMTEL} is defined
122      * for this ImsService. If it is set, it will be removed during sanitization before the final
123      * capabilities bitfield is sent back to the framework.
124      * @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be
125      * adding other capabilities in a central location, so track this capability here as well.
126      */
127     public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0;
128 
129     /**
130      * This ImsService supports the capability to create SIP delegates for other IMS applications
131      * to use to proxy SIP messaging traffic through it.
132      * <p>
133      * In order for the framework to report SipDelegate creation as being available for this
134      * ImsService implementation, this ImsService must report this capability flag in
135      * {@link #getImsServiceCapabilities()}, {@link #getSipTransport(int)} must not return null, and
136      * this ImsService MUST report the ability to create both {@link ImsFeature#FEATURE_MMTEL} and
137      * {@link ImsFeature#FEATURE_RCS} features.
138      */
139     public static final long CAPABILITY_SIP_DELEGATE_CREATION = 1 << 1;
140 
141     /**
142      * Used for internal correctness checks of capabilities set by the ImsService implementation and
143      * tracks the index of the largest defined flag in the capabilities long.
144      * @hide
145      */
146     public static final long CAPABILITY_MAX_INDEX =
147             Long.numberOfTrailingZeros(CAPABILITY_SIP_DELEGATE_CREATION);
148 
149     /**
150      * @hide
151      */
152     @LongDef(flag = true,
153             prefix = "CAPABILITY_",
154             value = {
155                     // CAPABILITY_EMERGENCY_OVER_MMTEL is not included here because it is managed by
156                     // whether or not ImsFeature.FEATURE_EMERGENCY_MMTEL feature is set and should
157                     // not be set by users of ImsService.
158                     CAPABILITY_SIP_DELEGATE_CREATION
159             })
160     @Retention(RetentionPolicy.SOURCE)
161     public @interface ImsServiceCapability {}
162 
163     /**
164      * Used for logging purposes, see {@link #getCapabilitiesString(long)}
165      * @hide
166      */
167     private static final Map<Long, String> CAPABILITIES_LOG_MAP = new HashMap<Long, String>() {{
168             put(CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL");
169             put(CAPABILITY_SIP_DELEGATE_CREATION, "SIP_DELEGATE_CREATION");
170         }};
171 
172     /**
173      * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
174      * @hide
175      */
176     public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
177 
178     // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
179     // slot.
180     // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
181     // call ImsFeature#onFeatureRemoved.
182     private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
183 
184     // A map of slot id -> boolean array, where each entry in the boolean array corresponds to an
185     // ImsFeature that was created for a slot id and not a sub id for backwards compatibility
186     // purposes.
187     private final SparseArray<SparseBooleanArray> mCreateImsFeatureWithSlotIdFlagMap =
188             new SparseArray<>();
189 
190     private IImsServiceControllerListener mListener;
191     private Executor mExecutor;
192 
193     /**
194      * Create a new ImsService.
195      * <p>
196      * Method stubs called from the framework will be called asynchronously. Vendor specifies the
197      * {@link Executor} that the methods stubs will be called. If mExecutor is set to null by
198      * vendor use Runnable::run.
199      */
ImsService()200     public ImsService() {
201         mExecutor = ImsService.this.getExecutor();
202         if (mExecutor == null) {
203             mExecutor = Runnable::run;
204         }
205     }
206 
207     /**
208      * Listener that notifies the framework of ImsService changes.
209      * @hide
210      */
211     public static class Listener extends IImsServiceControllerListener.Stub {
212         /**
213          * The IMS features that this ImsService supports has changed.
214          * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s
215          *   that this ImsService supports. This may trigger the addition/removal of feature
216          *   in this service.
217          */
onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)218         public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
219         }
220     }
221 
222     /**
223      * @hide
224      */
225     protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
226         @Override
227         public void setListener(IImsServiceControllerListener l) {
228             mListener = l;
229         }
230 
231         @Override
232         public IImsMmTelFeature createMmTelFeature(int slotId, int subId) {
233             MmTelFeature f  = (MmTelFeature) getImsFeature(slotId, ImsFeature.FEATURE_MMTEL);
234             if (f == null) {
235                 return executeMethodAsyncForResult(() -> createMmTelFeatureInternal(slotId, subId),
236                         "createMmTelFeature");
237             } else {
238                 return f.getBinder();
239             }
240         }
241 
242         @Override
243         public IImsMmTelFeature createEmergencyOnlyMmTelFeature(int slotId) {
244             MmTelFeature f  = (MmTelFeature) getImsFeature(slotId, ImsFeature.FEATURE_MMTEL);
245             if (f == null) {
246                 return executeMethodAsyncForResult(() -> createEmergencyOnlyMmTelFeatureInternal(
247                         slotId), "createEmergencyOnlyMmTelFeature");
248             } else {
249                 return f.getBinder();
250             }
251         }
252 
253         @Override
254         public IImsRcsFeature createRcsFeature(int slotId, int subId) {
255             RcsFeature f  = (RcsFeature) getImsFeature(slotId, ImsFeature.FEATURE_RCS);
256             if (f == null) {
257                 return executeMethodAsyncForResult(() ->
258                         createRcsFeatureInternal(slotId, subId), "createRcsFeature");
259             } else {
260                 return f.getBinder();
261             }
262         }
263 
264         @Override
265         public void addFeatureStatusCallback(int slotId, int featureType,
266                 IImsFeatureStatusCallback c) {
267             executeMethodAsync(() -> ImsService.this.addImsFeatureStatusCallback(
268                     slotId, featureType, c), "addFeatureStatusCallback");
269         }
270 
271         @Override
272         public void removeFeatureStatusCallback(int slotId, int featureType,
273                 IImsFeatureStatusCallback c) {
274             executeMethodAsync(() -> ImsService.this.removeImsFeatureStatusCallback(
275                     slotId, featureType, c), "removeFeatureStatusCallback");
276         }
277 
278         @Override
279         public void removeImsFeature(int slotId, int featureType, boolean changeSubId) {
280             if (changeSubId && isImsFeatureCreatedForSlot(slotId, featureType)) {
281                 Log.w(LOG_TAG, "Do not remove Ims feature for compatibility");
282                 return;
283             }
284             executeMethodAsync(() -> ImsService.this.removeImsFeature(slotId, featureType),
285                     "removeImsFeature");
286             setImsFeatureCreatedForSlot(slotId, featureType, false);
287         }
288 
289         @Override
290         public ImsFeatureConfiguration querySupportedImsFeatures() {
291             return executeMethodAsyncForResult(() -> ImsService.this.querySupportedImsFeatures(),
292                     "ImsFeatureConfiguration");
293         }
294 
295         @Override
296         public long getImsServiceCapabilities() {
297             return executeMethodAsyncForResult(() -> {
298                 long caps = ImsService.this.getImsServiceCapabilities();
299                 long sanitizedCaps = sanitizeCapabilities(caps);
300                 if (caps != sanitizedCaps) {
301                     Log.w(LOG_TAG, "removing invalid bits from field: 0x"
302                             + Long.toHexString(caps ^ sanitizedCaps));
303                 }
304                 return sanitizedCaps;
305             }, "getImsServiceCapabilities");
306         }
307 
308         @Override
309         public void notifyImsServiceReadyForFeatureCreation() {
310             executeMethodAsync(() -> ImsService.this.readyForFeatureCreation(),
311                     "notifyImsServiceReadyForFeatureCreation");
312         }
313 
314         @Override
315         public IImsConfig getConfig(int slotId, int subId) {
316             return executeMethodAsyncForResult(() -> {
317                 ImsConfigImplBase c =
318                         ImsService.this.getConfigForSubscription(slotId, subId);
319                 if (c != null) {
320                     c.setDefaultExecutor(mExecutor);
321                     return c.getIImsConfig();
322                 } else {
323                     return null;
324                 }
325             }, "getConfig");
326         }
327 
328         @Override
329         public IImsRegistration getRegistration(int slotId, int subId) {
330             return executeMethodAsyncForResult(() -> {
331                 ImsRegistrationImplBase r =
332                         ImsService.this.getRegistrationForSubscription(slotId, subId);
333                 if (r != null) {
334                     r.setDefaultExecutor(mExecutor);
335                     return r.getBinder();
336                 } else {
337                     return null;
338                 }
339             }, "getRegistration");
340         }
341 
342         @Override
343         public ISipTransport getSipTransport(int slotId) {
344             return executeMethodAsyncForResult(() -> {
345                 SipTransportImplBase s =  ImsService.this.getSipTransport(slotId);
346                 if (s != null) {
347                     s.setDefaultExecutor(mExecutor);
348                     return s.getBinder();
349                 } else {
350                     return null;
351                 }
352             }, "getSipTransport");
353         }
354 
355         @Override
356         public void enableIms(int slotId, int subId) {
357             executeMethodAsync(() ->
358                     ImsService.this.enableImsForSubscription(slotId, subId), "enableIms");
359         }
360 
361         @Override
362         public void disableIms(int slotId, int subId) {
363             executeMethodAsync(() ->
364                     ImsService.this.disableImsForSubscription(slotId, subId), "disableIms");
365         }
366 
367         // Call the methods with a clean calling identity on the executor and wait indefinitely for
368         // the future to return.
369         private void executeMethodAsync(Runnable r, String errorLogName) {
370             try {
371                 CompletableFuture.runAsync(
372                         () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
373             } catch (CancellationException | CompletionException e) {
374                 Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
375                         + e.getMessage());
376             }
377         }
378 
379         private <T> T executeMethodAsyncForResult(Supplier<T> r, String errorLogName) {
380             CompletableFuture<T> future = CompletableFuture.supplyAsync(
381                     () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
382             try {
383                 return future.get();
384             } catch (ExecutionException | InterruptedException e) {
385                 Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
386                         + e.getMessage());
387                 return null;
388             }
389         }
390     };
391 
392     /**
393      * @hide
394      */
395     @Override
onBind(Intent intent)396     public IBinder onBind(Intent intent) {
397         if(SERVICE_INTERFACE.equals(intent.getAction())) {
398             Log.i(LOG_TAG, "ImsService Bound.");
399             return mImsServiceController;
400         }
401         return null;
402     }
403 
createMmTelFeatureInternal(int slotId, int subscriptionId)404     private IImsMmTelFeature createMmTelFeatureInternal(int slotId, int subscriptionId) {
405         MmTelFeature f = createMmTelFeatureForSubscription(slotId, subscriptionId);
406         if (f != null) {
407             setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
408             f.setDefaultExecutor(mExecutor);
409             return f.getBinder();
410         } else {
411             Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
412             return null;
413         }
414     }
415 
createEmergencyOnlyMmTelFeatureInternal(int slotId)416     private IImsMmTelFeature createEmergencyOnlyMmTelFeatureInternal(int slotId) {
417         MmTelFeature f = createEmergencyOnlyMmTelFeature(slotId);
418         if (f != null) {
419             setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
420             f.setDefaultExecutor(mExecutor);
421             return f.getBinder();
422         } else {
423             Log.e(LOG_TAG, "createEmergencyOnlyMmTelFeatureInternal: null feature returned.");
424             return null;
425         }
426     }
427 
createRcsFeatureInternal(int slotId, int subI)428     private IImsRcsFeature createRcsFeatureInternal(int slotId, int subI) {
429         RcsFeature f = createRcsFeatureForSubscription(slotId, subI);
430         if (f != null) {
431             f.setDefaultExecutor(mExecutor);
432             setupFeature(f, slotId, ImsFeature.FEATURE_RCS);
433             return f.getBinder();
434         } else {
435             Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
436             return null;
437         }
438     }
439 
setupFeature(ImsFeature f, int slotId, int featureType)440     private void setupFeature(ImsFeature f, int slotId, int featureType) {
441         f.initialize(this, slotId);
442         addImsFeature(slotId, featureType, f);
443     }
444 
addImsFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c)445     private void addImsFeatureStatusCallback(int slotId, int featureType,
446             IImsFeatureStatusCallback c) {
447         synchronized (mFeaturesBySlot) {
448             // get ImsFeature associated with the slot/feature
449             SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
450             if (features == null) {
451                 Log.w(LOG_TAG, "Can not add ImsFeatureStatusCallback - no features on slot "
452                         + slotId);
453                 return;
454             }
455             ImsFeature f = features.get(featureType);
456             if (f != null) {
457                 f.addImsFeatureStatusCallback(c);
458             }
459         }
460     }
461 
removeImsFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c)462     private void removeImsFeatureStatusCallback(int slotId, int featureType,
463             IImsFeatureStatusCallback c) {
464         synchronized (mFeaturesBySlot) {
465             // get ImsFeature associated with the slot/feature
466             SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
467             if (features == null) {
468                 Log.w(LOG_TAG, "Can not remove ImsFeatureStatusCallback - no features on slot "
469                         + slotId);
470                 return;
471             }
472             ImsFeature f = features.get(featureType);
473             if (f != null) {
474                 f.removeImsFeatureStatusCallback(c);
475             }
476         }
477     }
478 
addImsFeature(int slotId, int featureType, ImsFeature f)479     private void addImsFeature(int slotId, int featureType, ImsFeature f) {
480         synchronized (mFeaturesBySlot) {
481             // Get SparseArray for Features, by querying slot Id
482             SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
483             if (features == null) {
484                 // Populate new SparseArray of features if it doesn't exist for this slot yet.
485                 features = new SparseArray<>();
486                 mFeaturesBySlot.put(slotId, features);
487             }
488             features.put(featureType, f);
489         }
490     }
491 
removeImsFeature(int slotId, int featureType)492     private void removeImsFeature(int slotId, int featureType) {
493         synchronized (mFeaturesBySlot) {
494             // get ImsFeature associated with the slot/feature
495             SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
496             if (features == null) {
497                 Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
498                         + slotId);
499                 return;
500             }
501             ImsFeature f = features.get(featureType);
502             if (f == null) {
503                 Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
504                         + featureType + " exists on slot " + slotId);
505                 return;
506             }
507             f.onFeatureRemoved();
508             features.remove(featureType);
509         }
510 
511     }
512 
513     /**
514      * @hide
515      */
516     @VisibleForTesting
getImsFeature(int slotId, int featureType)517     public ImsFeature getImsFeature(int slotId, int featureType) {
518         synchronized (mFeaturesBySlot) {
519             // Get SparseArray for Features, by querying slot Id
520             SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
521             if (features == null) {
522                 return null;
523             }
524             return features.get(featureType);
525         }
526     }
527 
setImsFeatureCreatedForSlot(int slotId, @ImsFeature.FeatureType int featureType, boolean createdForSlot)528     private void setImsFeatureCreatedForSlot(int slotId,
529             @ImsFeature.FeatureType int featureType, boolean createdForSlot) {
530         synchronized (mCreateImsFeatureWithSlotIdFlagMap) {
531             getImsFeatureCreatedForSlot(slotId).put(featureType, createdForSlot);
532         }
533     }
534 
535     /**
536      * @hide
537      */
538     @VisibleForTesting
isImsFeatureCreatedForSlot(int slotId, @ImsFeature.FeatureType int featureType)539     public boolean isImsFeatureCreatedForSlot(int slotId,
540             @ImsFeature.FeatureType int featureType) {
541         synchronized (mCreateImsFeatureWithSlotIdFlagMap) {
542             return getImsFeatureCreatedForSlot(slotId).get(featureType);
543         }
544     }
545 
getImsFeatureCreatedForSlot(int slotId)546     private SparseBooleanArray getImsFeatureCreatedForSlot(int slotId) {
547         SparseBooleanArray createFlag = mCreateImsFeatureWithSlotIdFlagMap.get(slotId);
548         if (createFlag == null) {
549             createFlag = new SparseBooleanArray();
550             mCreateImsFeatureWithSlotIdFlagMap.put(slotId, createFlag);
551         }
552         return createFlag;
553     }
554 
555     /**
556      * When called, provide the {@link ImsFeatureConfiguration} that this {@link ImsService}
557      * currently supports. This will trigger the framework to set up the {@link ImsFeature}s that
558      * correspond to the {@link ImsFeature}s configured here.
559      *
560      * Use {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} to change the supported
561      * {@link ImsFeature}s.
562      *
563      * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports.
564      */
querySupportedImsFeatures()565     public ImsFeatureConfiguration querySupportedImsFeatures() {
566         // Return empty for base implementation
567         return new ImsFeatureConfiguration();
568     }
569 
570     /**
571      * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated
572      * features, that this {@link ImsService} supports. This may trigger the framework to add/remove
573      * new ImsFeatures, depending on the configuration.
574      */
onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)575     public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
576             throws RemoteException {
577         if (mListener == null) {
578             throw new IllegalStateException("Framework is not ready");
579         }
580         mListener.onUpdateSupportedImsFeatures(c);
581     }
582 
583     /**
584      * The optional capabilities that this ImsService supports.
585      * <p>
586      * This should be a static configuration and should not change at runtime.
587      * @return The optional static capabilities of this ImsService implementation.
588      */
589     // ImsService follows a different convention, since it is a stub class. The on* methods are
590     // final and call back into the framework with a state update.
591     @SuppressLint("OnNameExpected")
getImsServiceCapabilities()592     public @ImsServiceCapability long getImsServiceCapabilities() {
593         // Stub implementation to be implemented by ImsService.
594         return 0L;
595     }
596 
597     /**
598      * The ImsService has been bound and is ready for ImsFeature creation based on the Features that
599      * the ImsService has registered for with the framework, either in the manifest or via
600      * {@link #querySupportedImsFeatures()}.
601      *
602      * The ImsService should use this signal instead of onCreate/onBind or similar to perform
603      * feature initialization because the framework may bind to this service multiple times to
604      * query the ImsService's {@link ImsFeatureConfiguration} via
605      * {@link #querySupportedImsFeatures()}before creating features.
606      */
readyForFeatureCreation()607     public void readyForFeatureCreation() {
608     }
609 
610     /**
611      * The framework has enabled IMS for the subscription specified, the ImsService should register
612      * for IMS and perform all appropriate initialization to bring up all ImsFeatures.
613      *
614      * @param slotId The slot ID that IMS will be enabled for.
615      * @param subscriptionId The subscription ID that IMS will be enabled for.
616      */
enableImsForSubscription(int slotId, int subscriptionId)617     public void enableImsForSubscription(int slotId, int subscriptionId) {
618         enableIms(slotId);
619     }
620 
621     /**
622      * The framework has disabled IMS for the subscription specified. The ImsService must deregister
623      * for IMS and set capability status to false for all ImsFeatures.
624      * @param slotId The slot ID that IMS will be disabled for.
625      * @param subscriptionId The subscription ID that IMS will be disabled for.
626      */
disableImsForSubscription(int slotId, int subscriptionId)627     public void disableImsForSubscription(int slotId, int subscriptionId) {
628         disableIms(slotId);
629     }
630 
631     /**
632      * The framework has enabled IMS for the slot specified, the ImsService should register for IMS
633      * and perform all appropriate initialization to bring up all ImsFeatures.
634      * @deprecated Use {@link #enableImsForSubscription} instead.
635      */
636     @Deprecated
enableIms(int slotId)637     public void enableIms(int slotId) {
638     }
639 
640     /**
641      * The framework has disabled IMS for the slot specified. The ImsService must deregister for IMS
642      * and set capability status to false for all ImsFeatures.
643      * @deprecated Use {@link #disableImsForSubscription} instead.
644      */
645     @Deprecated
disableIms(int slotId)646     public void disableIms(int slotId) {
647     }
648 
649     /**
650      * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
651      * specified subscription.
652      *
653      * @param subscriptionId The subscription ID that the MMTEL Feature is being created for.
654      * @return The newly created {@link MmTelFeature} associated with the subscription or null if
655      * the feature is not supported.
656      */
createMmTelFeatureForSubscription(int slotId, int subscriptionId)657     public @Nullable MmTelFeature createMmTelFeatureForSubscription(int slotId,
658             int subscriptionId) {
659         setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_MMTEL, true);
660         return createMmTelFeature(slotId);
661     }
662 
663     /**
664      * When called, the framework is requesting that a new {@link RcsFeature} is created for the
665      * specified subscription.
666      *
667      * @param subscriptionId The subscription ID that the RCS Feature is being created for.
668      * @return The newly created {@link RcsFeature} associated with the subscription or null if the
669      * feature is not supported.
670      */
createRcsFeatureForSubscription(int slotId, int subscriptionId)671     public @Nullable RcsFeature createRcsFeatureForSubscription(int slotId, int subscriptionId) {
672         setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_RCS, true);
673         return createRcsFeature(slotId);
674     }
675 
676     /**
677      * When called, the framework is requesting that a new emergency-only {@link MmTelFeature} is
678      * created for the specified slot. For emergency calls, there is no known Subscription Id.
679      *
680      * @param slotId The slot ID that the MMTEL Feature is being created for.
681      * @return An MmTelFeature instance to be used for the slot ID when there is not
682      * subscription inserted. Only requested when there is no subscription active on
683      * the specified slot.
684      */
createEmergencyOnlyMmTelFeature(int slotId)685     public @Nullable MmTelFeature createEmergencyOnlyMmTelFeature(int slotId) {
686         setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_MMTEL, true);
687         return createMmTelFeature(slotId);
688     }
689 
690     /**
691      * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
692      * specified slot.
693      * @deprecated Use {@link #createMmTelFeatureForSubscription} instead
694      *
695      * @param slotId The slot ID that the MMTEL Feature is being created for.
696      * @return The newly created {@link MmTelFeature} associated with the slot or null if the
697      * feature is not supported.
698      */
699     @Deprecated
createMmTelFeature(int slotId)700     public MmTelFeature createMmTelFeature(int slotId) {
701         return null;
702     }
703 
704     /**
705      * When called, the framework is requesting that a new {@link RcsFeature} is created for the
706      * specified slot.
707      * @deprecated Use {@link #createRcsFeatureForSubscription} instead
708      *
709      * @param slotId The slot ID that the RCS Feature is being created for.
710      * @return The newly created {@link RcsFeature} associated with the slot or null if the feature
711      * is not supported.
712      */
713     @Deprecated
createRcsFeature(int slotId)714     public RcsFeature createRcsFeature(int slotId) {
715         return null;
716     }
717 
718     /**
719      * Return the {@link ImsConfigImplBase} implementation associated with the provided
720      * subscription. This will be used by the platform to get/set specific IMS related
721      * configurations.
722      *
723      * @param subscriptionId The subscription ID that the IMS configuration is associated with.
724      * @return ImsConfig implementation that is associated with the specified subscription.
725      */
getConfigForSubscription(int slotId, int subscriptionId)726     public @NonNull ImsConfigImplBase getConfigForSubscription(int slotId, int subscriptionId) {
727         return getConfig(slotId);
728     }
729 
730     /**
731      * Return the {@link ImsRegistrationImplBase} implementation associated with the provided
732      * subscription.
733      *
734      * @param subscriptionId The subscription ID that is associated with the IMS Registration.
735      * @return the ImsRegistration implementation associated with the subscription.
736      */
getRegistrationForSubscription(int slotId, int subscriptionId)737     public @NonNull ImsRegistrationImplBase getRegistrationForSubscription(int slotId,
738             int subscriptionId) {
739         return getRegistration(slotId);
740     }
741 
742     /**
743      * Return the {@link ImsConfigImplBase} implementation associated with the provided slot. This
744      * will be used by the platform to get/set specific IMS related configurations.
745      * @deprecated use {@link #getConfigForSubscription} instead.
746      *
747      * @param slotId The slot that the IMS configuration is associated with.
748      * @return ImsConfig implementation that is associated with the specified slot.
749      */
750     @Deprecated
getConfig(int slotId)751     public ImsConfigImplBase getConfig(int slotId) {
752         return new ImsConfigImplBase();
753     }
754 
755     /**
756      * Return the {@link ImsRegistrationImplBase} implementation associated with the provided slot.
757      * @deprecated use  {@link #getRegistrationForSubscription} instead.
758      *
759      * @param slotId The slot that is associated with the IMS Registration.
760      * @return the ImsRegistration implementation associated with the slot.
761      */
762     @Deprecated
getRegistration(int slotId)763     public ImsRegistrationImplBase getRegistration(int slotId) {
764         return new ImsRegistrationImplBase();
765     }
766 
767     /**
768      * Return the {@link SipTransportImplBase} implementation associated with the provided slot.
769      * <p>
770      * This is an optional interface used for devices that must support IMS single registration and
771      * proxy SIP traffic to remote IMS applications. If this is not supported for this IMS service,
772      * this method should return {@code null}. If this feature is supported, then this method must
773      * never be {@code null} and the optional ImsService capability flag
774      * {@link #CAPABILITY_SIP_DELEGATE_CREATION} must be set in
775      * {@link #getImsServiceCapabilities()}. Otherwise the framework will assume this feature is not
776      * supported for this ImsService.
777      * @param slotId The slot that is associated with the SipTransport implementation.
778      * @return the SipTransport implementation for the specified slot.
779      */
780     // ImsService follows a different convention, since it is a stub class. The on* methods are
781     // final and call back into the framework with a state update.
782     @SuppressLint("OnNameExpected")
getSipTransport(int slotId)783     public @Nullable SipTransportImplBase getSipTransport(int slotId) {
784         // Stub implementation for ImsServices that do not support SipTransport.
785         return null;
786     }
787 
sanitizeCapabilities(@msServiceCapability long caps)788     private static long sanitizeCapabilities(@ImsServiceCapability long caps) {
789         long filter = 0xFFFFFFFFFFFFFFFFL;
790         // pad the "allowed" set with zeros
791         filter <<= CAPABILITY_MAX_INDEX + 1;
792         // remove values above the allowed set.
793         caps &= ~filter;
794         // CAPABILITY_EMERGENCY_OVER_MMTEL should also not be set here, will be set by telephony
795         // internally.
796         caps &= ~CAPABILITY_EMERGENCY_OVER_MMTEL;
797         return caps;
798     }
799 
800     /**
801      * @return A string representation of the ImsService capabilities for logging.
802      * @hide
803      */
getCapabilitiesString(@msServiceCapability long caps)804     public static String getCapabilitiesString(@ImsServiceCapability long caps) {
805         StringBuffer result = new StringBuffer();
806         result.append("capabilities={ ");
807         // filter incrementally fills 0s from  left to right. This is used to keep filtering out
808         // more bits in the long until the remaining leftmost bits are all zero.
809         long filter = 0xFFFFFFFFFFFFFFFFL;
810         // position of iterator to potentially print capability.
811         long i = 0;
812         while ((caps & filter) != 0 && i <= 63) {
813             long bitToCheck = (1L << i);
814             if ((caps & bitToCheck) != 0) {
815                 result.append(CAPABILITIES_LOG_MAP.getOrDefault(bitToCheck, bitToCheck + "?"));
816                 result.append(" ");
817             }
818             // shift left by one and fill in another 1 on the leftmost bit.
819             filter <<= 1;
820             i++;
821         }
822         result.append("}");
823         return result.toString();
824     }
825 
826     /**
827      * The ImsService will now be able to define an Executor that the ImsService can be used to
828      * execute the methods. By default all ImsService level method calls will use this Executor.
829      * The ImsService has set the default executor as Runnable::run,
830      * Should be override or default executor will be used.
831      *  @return an Executor used to execute methods called remotely by the framework.
832      */
getExecutor()833     public @NonNull Executor getExecutor() {
834         return Runnable::run;
835     }
836 }
837