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.Map;
54 import java.util.NoSuchElementException;
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      * This ImsService supports the terminal based call waiting service.
143      * <p>
144      * In order for the IMS service to support the service, IMS service shall
145      * override {@link MmTelFeature#setTerminalBasedCallWaitingStatus}.
146      * If ImsService has this capability, Android platform will handle the synchronization
147      * between the network based call waiting service over circuit-switched networks and the
148      * terminal based call waiting service of IMS service, and will handle the received
149      * circuit-switched waiting calls. Otherwise, this functionality of Android platform shall
150      * be disabled.
151      */
152     public static final long CAPABILITY_TERMINAL_BASED_CALL_WAITING = 1 << 2;
153 
154     /**
155      * Used for internal correctness checks of capabilities set by the ImsService implementation and
156      * tracks the index of the largest defined flag in the capabilities long.
157      * @hide
158      */
159     public static final long CAPABILITY_MAX_INDEX =
160             Long.numberOfTrailingZeros(CAPABILITY_TERMINAL_BASED_CALL_WAITING);
161 
162     /**
163      * @hide
164      */
165     @LongDef(flag = true,
166             prefix = "CAPABILITY_",
167             value = {
168                     // CAPABILITY_EMERGENCY_OVER_MMTEL is not included here because it is managed by
169                     // whether or not ImsFeature.FEATURE_EMERGENCY_MMTEL feature is set and should
170                     // not be set by users of ImsService.
171                     CAPABILITY_SIP_DELEGATE_CREATION,
172                     CAPABILITY_TERMINAL_BASED_CALL_WAITING
173             })
174     @Retention(RetentionPolicy.SOURCE)
175     public @interface ImsServiceCapability {}
176 
177     /**
178      * Used for logging purposes, see {@link #getCapabilitiesString(long)}
179      * @hide
180      */
181     private static final Map<Long, String> CAPABILITIES_LOG_MAP = Map.of(
182             CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL",
183             CAPABILITY_SIP_DELEGATE_CREATION, "SIP_DELEGATE_CREATION");
184 
185     /**
186      * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
187      * @hide
188      */
189     public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
190 
191     // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
192     // slot.
193     // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
194     // call ImsFeature#onFeatureRemoved.
195     private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
196 
197     // A map of slot id -> boolean array, where each entry in the boolean array corresponds to an
198     // ImsFeature that was created for a slot id and not a sub id for backwards compatibility
199     // purposes.
200     private final SparseArray<SparseBooleanArray> mCreateImsFeatureWithSlotIdFlagMap =
201             new SparseArray<>();
202 
203     private IImsServiceControllerListener mListener;
204     private final Object mListenerLock = new Object();
205     private final Object mExecutorLock = new Object();
206     private Executor mExecutor;
207 
208     /**
209      * Create a new ImsService.
210      * <p>
211      * Method stubs called from the framework will be called asynchronously. Vendor specifies the
212      * {@link Executor} that the methods stubs will be called. If mExecutor is set to null by
213      * vendor use Runnable::run.
214      */
ImsService()215     public ImsService() {
216     }
217 
218     /**
219      * Listener that notifies the framework of ImsService changes.
220      * @hide
221      */
222     public static class Listener extends IImsServiceControllerListener.Stub {
223         /**
224          * The IMS features that this ImsService supports has changed.
225          * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s
226          *   that this ImsService supports. This may trigger the addition/removal of feature
227          *   in this service.
228          */
onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)229         public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
230         }
231     }
232 
233     /**
234      * @hide
235      */
236     protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
237         @Override
238         public void setListener(IImsServiceControllerListener l) {
239             synchronized (mListenerLock) {
240                 if (mListener != null && mListener.asBinder().isBinderAlive()) {
241                     try {
242                         mListener.asBinder().unlinkToDeath(mDeathRecipient, 0);
243                     } catch (NoSuchElementException e) {
244                         Log.w(LOG_TAG, "IImsServiceControllerListener does not exist");
245                     }
246                 }
247 
248                 mListener = l;
249                 if (mListener == null) {
250                     executeMethodAsync(() -> releaseResource(), "releaseResource");
251                     return;
252                 }
253 
254                 try {
255                     mListener.asBinder().linkToDeath(mDeathRecipient, 0);
256                     Log.i(LOG_TAG, "setListener: register linkToDeath");
257                 } catch (RemoteException e) {
258                     // RemoteException means target binder process was crashed
259                     // release resource
260                     executeMethodAsync(() -> releaseResource(), "releaseResource");
261                 }
262             }
263         }
264 
265         @Override
266         public IImsMmTelFeature createMmTelFeature(int slotId, int subId) {
267             MmTelFeature f  = (MmTelFeature) getImsFeature(slotId, ImsFeature.FEATURE_MMTEL);
268             if (f == null) {
269                 return executeMethodAsyncForResult(() -> createMmTelFeatureInternal(slotId, subId),
270                         "createMmTelFeature");
271             } else {
272                 return f.getBinder();
273             }
274         }
275 
276         @Override
277         public IImsMmTelFeature createEmergencyOnlyMmTelFeature(int slotId) {
278             MmTelFeature f  = (MmTelFeature) getImsFeature(slotId, ImsFeature.FEATURE_MMTEL);
279             if (f == null) {
280                 return executeMethodAsyncForResult(() -> createEmergencyOnlyMmTelFeatureInternal(
281                         slotId), "createEmergencyOnlyMmTelFeature");
282             } else {
283                 return f.getBinder();
284             }
285         }
286 
287         @Override
288         public IImsRcsFeature createRcsFeature(int slotId, int subId) {
289             RcsFeature f  = (RcsFeature) getImsFeature(slotId, ImsFeature.FEATURE_RCS);
290             if (f == null) {
291                 return executeMethodAsyncForResult(() ->
292                         createRcsFeatureInternal(slotId, subId), "createRcsFeature");
293             } else {
294                 return f.getBinder();
295             }
296         }
297 
298         @Override
299         public void addFeatureStatusCallback(int slotId, int featureType,
300                 IImsFeatureStatusCallback c) {
301             executeMethodAsync(() -> ImsService.this.addImsFeatureStatusCallback(
302                     slotId, featureType, c), "addFeatureStatusCallback");
303         }
304 
305         @Override
306         public void removeFeatureStatusCallback(int slotId, int featureType,
307                 IImsFeatureStatusCallback c) {
308             executeMethodAsync(() -> ImsService.this.removeImsFeatureStatusCallback(
309                     slotId, featureType, c), "removeFeatureStatusCallback");
310         }
311 
312         @Override
313         public void removeImsFeature(int slotId, int featureType, boolean changeSubId) {
314             if (changeSubId && isImsFeatureCreatedForSlot(slotId, featureType)) {
315                 Log.w(LOG_TAG, "Do not remove Ims feature for compatibility");
316                 return;
317             }
318             executeMethodAsync(() -> ImsService.this.removeImsFeature(slotId, featureType),
319                     "removeImsFeature");
320             setImsFeatureCreatedForSlot(slotId, featureType, false);
321         }
322 
323         @Override
324         public ImsFeatureConfiguration querySupportedImsFeatures() {
325             return executeMethodAsyncForResult(() -> ImsService.this.querySupportedImsFeatures(),
326                     "ImsFeatureConfiguration");
327         }
328 
329         @Override
330         public long getImsServiceCapabilities() {
331             return executeMethodAsyncForResult(() -> {
332                 long caps = ImsService.this.getImsServiceCapabilities();
333                 long sanitizedCaps = sanitizeCapabilities(caps);
334                 if (caps != sanitizedCaps) {
335                     Log.w(LOG_TAG, "removing invalid bits from field: 0x"
336                             + Long.toHexString(caps ^ sanitizedCaps));
337                 }
338                 return sanitizedCaps;
339             }, "getImsServiceCapabilities");
340         }
341 
342         @Override
343         public void notifyImsServiceReadyForFeatureCreation() {
344             executeMethodAsync(() -> ImsService.this.readyForFeatureCreation(),
345                     "notifyImsServiceReadyForFeatureCreation");
346         }
347 
348         @Override
349         public IImsConfig getConfig(int slotId, int subId) {
350             return executeMethodAsyncForResult(() -> {
351                 ImsConfigImplBase c =
352                         ImsService.this.getConfigForSubscription(slotId, subId);
353                 if (c != null) {
354                     c.setDefaultExecutor(getCachedExecutor());
355                     return c.getIImsConfig();
356                 } else {
357                     return null;
358                 }
359             }, "getConfig");
360         }
361 
362         @Override
363         public IImsRegistration getRegistration(int slotId, int subId) {
364             return executeMethodAsyncForResult(() -> {
365                 ImsRegistrationImplBase r =
366                         ImsService.this.getRegistrationForSubscription(slotId, subId);
367                 if (r != null) {
368                     r.setDefaultExecutor(getCachedExecutor());
369                     return r.getBinder();
370                 } else {
371                     return null;
372                 }
373             }, "getRegistration");
374         }
375 
376         @Override
377         public ISipTransport getSipTransport(int slotId) {
378             return executeMethodAsyncForResult(() -> {
379                 SipTransportImplBase s =  ImsService.this.getSipTransport(slotId);
380                 if (s != null) {
381                     s.setDefaultExecutor(getCachedExecutor());
382                     return s.getBinder();
383                 } else {
384                     return null;
385                 }
386             }, "getSipTransport");
387         }
388 
389         @Override
390         public void enableIms(int slotId, int subId) {
391             executeMethodAsync(() ->
392                     ImsService.this.enableImsForSubscription(slotId, subId), "enableIms");
393         }
394 
395         @Override
396         public void disableIms(int slotId, int subId) {
397             executeMethodAsync(() ->
398                     ImsService.this.disableImsForSubscription(slotId, subId), "disableIms");
399         }
400 
401         @Override
402         public void resetIms(int slotId, int subId) {
403             executeMethodAsync(() ->
404                     ImsService.this.resetImsInternal(slotId, subId), "resetIms");
405         }
406     };
407 
408     private final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
409         @Override
410         public void binderDied() {
411             Log.w(LOG_TAG,
412                     "IImsServiceControllerListener binder to framework has died. Cleaning up");
413             executeMethodAsync(() -> releaseResource(), "releaseResource");
414         }
415     };
416 
417     /**
418      * @hide
419      */
420     @Override
onBind(Intent intent)421     public IBinder onBind(Intent intent) {
422         if(SERVICE_INTERFACE.equals(intent.getAction())) {
423             Log.i(LOG_TAG, "ImsService Bound.");
424             return mImsServiceController;
425         }
426         return null;
427     }
428 
getCachedExecutor()429     private Executor getCachedExecutor() {
430         synchronized (mExecutorLock) {
431             if (mExecutor == null) {
432                 Executor e = ImsService.this.getExecutor();
433                 mExecutor = (e != null) ? e : Runnable::run;
434             }
435             return mExecutor;
436         }
437     }
438 
createMmTelFeatureInternal(int slotId, int subscriptionId)439     private IImsMmTelFeature createMmTelFeatureInternal(int slotId, int subscriptionId) {
440         MmTelFeature f = createMmTelFeatureForSubscription(slotId, subscriptionId);
441         if (f != null) {
442             setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
443             f.setDefaultExecutor(getCachedExecutor());
444             return f.getBinder();
445         } else {
446             Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
447             return null;
448         }
449     }
450 
createEmergencyOnlyMmTelFeatureInternal(int slotId)451     private IImsMmTelFeature createEmergencyOnlyMmTelFeatureInternal(int slotId) {
452         MmTelFeature f = createEmergencyOnlyMmTelFeature(slotId);
453         if (f != null) {
454             setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
455             f.setDefaultExecutor(getCachedExecutor());
456             return f.getBinder();
457         } else {
458             Log.e(LOG_TAG, "createEmergencyOnlyMmTelFeatureInternal: null feature returned.");
459             return null;
460         }
461     }
462 
createRcsFeatureInternal(int slotId, int subId)463     private IImsRcsFeature createRcsFeatureInternal(int slotId, int subId) {
464         RcsFeature f = createRcsFeatureForSubscription(slotId, subId);
465         if (f != null) {
466             f.setDefaultExecutor(getCachedExecutor());
467             setupFeature(f, slotId, ImsFeature.FEATURE_RCS);
468             return f.getBinder();
469         } else {
470             Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
471             return null;
472         }
473     }
474 
setupFeature(ImsFeature f, int slotId, int featureType)475     private void setupFeature(ImsFeature f, int slotId, int featureType) {
476         f.initialize(this, slotId);
477         addImsFeature(slotId, featureType, f);
478     }
479 
addImsFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c)480     private void addImsFeatureStatusCallback(int slotId, int featureType,
481             IImsFeatureStatusCallback c) {
482         synchronized (mFeaturesBySlot) {
483             // get ImsFeature associated with the slot/feature
484             SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
485             if (features == null) {
486                 Log.w(LOG_TAG, "Can not add ImsFeatureStatusCallback - no features on slot "
487                         + slotId);
488                 return;
489             }
490             ImsFeature f = features.get(featureType);
491             if (f != null) {
492                 f.addImsFeatureStatusCallback(c);
493             }
494         }
495     }
496 
removeImsFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c)497     private void removeImsFeatureStatusCallback(int slotId, int featureType,
498             IImsFeatureStatusCallback c) {
499         synchronized (mFeaturesBySlot) {
500             // get ImsFeature associated with the slot/feature
501             SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
502             if (features == null) {
503                 Log.w(LOG_TAG, "Can not remove ImsFeatureStatusCallback - no features on slot "
504                         + slotId);
505                 return;
506             }
507             ImsFeature f = features.get(featureType);
508             if (f != null) {
509                 f.removeImsFeatureStatusCallback(c);
510             }
511         }
512     }
513 
addImsFeature(int slotId, int featureType, ImsFeature f)514     private void addImsFeature(int slotId, int featureType, ImsFeature f) {
515         synchronized (mFeaturesBySlot) {
516             // Get SparseArray for Features, by querying slot Id
517             SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
518             if (features == null) {
519                 // Populate new SparseArray of features if it doesn't exist for this slot yet.
520                 features = new SparseArray<>();
521                 mFeaturesBySlot.put(slotId, features);
522             }
523             features.put(featureType, f);
524         }
525     }
526 
removeImsFeature(int slotId, int featureType)527     private void removeImsFeature(int slotId, int featureType) {
528         // clear cached data
529         notifySubscriptionRemoved(slotId);
530 
531         synchronized (mFeaturesBySlot) {
532             // get ImsFeature associated with the slot/feature
533             SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
534             if (features == null) {
535                 Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
536                         + slotId);
537                 return;
538             }
539             ImsFeature f = features.get(featureType);
540             if (f == null) {
541                 Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
542                         + featureType + " exists on slot " + slotId);
543                 return;
544             }
545             f.onFeatureRemoved();
546             features.remove(featureType);
547         }
548     }
549 
550     /**
551      * @hide
552      */
553     @VisibleForTesting
getImsFeature(int slotId, int featureType)554     public ImsFeature getImsFeature(int slotId, int featureType) {
555         synchronized (mFeaturesBySlot) {
556             // Get SparseArray for Features, by querying slot Id
557             SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
558             if (features == null) {
559                 return null;
560             }
561             return features.get(featureType);
562         }
563     }
564 
setImsFeatureCreatedForSlot(int slotId, @ImsFeature.FeatureType int featureType, boolean createdForSlot)565     private void setImsFeatureCreatedForSlot(int slotId,
566             @ImsFeature.FeatureType int featureType, boolean createdForSlot) {
567         synchronized (mCreateImsFeatureWithSlotIdFlagMap) {
568             getImsFeatureCreatedForSlot(slotId).put(featureType, createdForSlot);
569         }
570     }
571 
572     /**
573      * @hide
574      */
575     @VisibleForTesting
isImsFeatureCreatedForSlot(int slotId, @ImsFeature.FeatureType int featureType)576     public boolean isImsFeatureCreatedForSlot(int slotId,
577             @ImsFeature.FeatureType int featureType) {
578         synchronized (mCreateImsFeatureWithSlotIdFlagMap) {
579             return getImsFeatureCreatedForSlot(slotId).get(featureType);
580         }
581     }
582 
getImsFeatureCreatedForSlot(int slotId)583     private SparseBooleanArray getImsFeatureCreatedForSlot(int slotId) {
584         SparseBooleanArray createFlag = mCreateImsFeatureWithSlotIdFlagMap.get(slotId);
585         if (createFlag == null) {
586             createFlag = new SparseBooleanArray();
587             mCreateImsFeatureWithSlotIdFlagMap.put(slotId, createFlag);
588         }
589         return createFlag;
590     }
591 
releaseResource()592     private void releaseResource() {
593         Log.w(LOG_TAG, "cleaning up features");
594         synchronized (mFeaturesBySlot) {
595             SparseArray<ImsFeature> features;
596             ImsFeature imsFeature;
597 
598             for (int i = 0; i < mFeaturesBySlot.size(); i++) {
599                 features = mFeaturesBySlot.valueAt(i);
600                 if (features == null) {
601                     continue;
602                 }
603 
604                 for (int index = 0; index < features.size(); index++) {
605                     imsFeature = features.valueAt(index);
606                     if (imsFeature != null) {
607                         imsFeature.onFeatureRemoved();
608                     }
609                 }
610                 features.clear();
611             }
612             mFeaturesBySlot.clear();
613         }
614     }
615 
616     // Call the methods with a clean calling identity on the executor and wait indefinitely for
617     // the future to return.
executeMethodAsync(Runnable r, String errorLogName)618     private void executeMethodAsync(Runnable r, String errorLogName) {
619         try {
620             CompletableFuture.runAsync(
621                     () -> TelephonyUtils.runWithCleanCallingIdentity(r),
622                     getCachedExecutor()).join();
623         } catch (CancellationException | CompletionException e) {
624             Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
625                     + e.getMessage());
626         }
627     }
628 
executeMethodAsyncForResult(Supplier<T> r, String errorLogName)629     private <T> T executeMethodAsyncForResult(Supplier<T> r, String errorLogName) {
630         CompletableFuture<T> future = CompletableFuture.supplyAsync(
631                 () -> TelephonyUtils.runWithCleanCallingIdentity(r), getCachedExecutor());
632         try {
633             return future.get();
634         } catch (ExecutionException | InterruptedException e) {
635             Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
636                     + e.getMessage());
637             return null;
638         }
639     }
640 
resetImsInternal(int slotId, int subId)641     private void resetImsInternal(int slotId, int subId) {
642         try {
643             resetIms(slotId);
644         } catch (UnsupportedOperationException e) {
645             disableImsForSubscription(slotId, subId);
646         }
647     }
648 
649     /**
650      * When called, provide the {@link ImsFeatureConfiguration} that this {@link ImsService}
651      * currently supports. This will trigger the framework to set up the {@link ImsFeature}s that
652      * correspond to the {@link ImsFeature}s configured here.
653      *
654      * Use {@link #onUpdateSupportedImsFeatures(ImsFeatureConfiguration)} to change the supported
655      * {@link ImsFeature}s.
656      *
657      * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports.
658      */
querySupportedImsFeatures()659     public ImsFeatureConfiguration querySupportedImsFeatures() {
660         // Return empty for base implementation
661         return new ImsFeatureConfiguration();
662     }
663 
664     /**
665      * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated
666      * features, that this {@link ImsService} supports. This may trigger the framework to add/remove
667      * new ImsFeatures, depending on the configuration.
668      */
onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)669     public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
670             throws RemoteException {
671         IImsServiceControllerListener l;
672         synchronized (mListenerLock) {
673             if (mListener == null) {
674                 throw new IllegalStateException("Framework is not ready");
675             }
676             l = mListener;
677         }
678         l.onUpdateSupportedImsFeatures(c);
679     }
680 
681     /**
682      * The optional capabilities that this ImsService supports.
683      * <p>
684      * This should be a static configuration and should not change at runtime.
685      * @return The optional static capabilities of this ImsService implementation.
686      */
687     // ImsService follows a different convention, since it is a stub class. The on* methods are
688     // final and call back into the framework with a state update.
689     @SuppressLint("OnNameExpected")
getImsServiceCapabilities()690     public @ImsServiceCapability long getImsServiceCapabilities() {
691         // Stub implementation to be implemented by ImsService.
692         return 0L;
693     }
694 
695     /**
696      * The ImsService has been bound and is ready for ImsFeature creation based on the Features that
697      * the ImsService has registered for with the framework, either in the manifest or via
698      * {@link #querySupportedImsFeatures()}.
699      *
700      * The ImsService should use this signal instead of onCreate/onBind or similar to perform
701      * feature initialization because the framework may bind to this service multiple times to
702      * query the ImsService's {@link ImsFeatureConfiguration} via
703      * {@link #querySupportedImsFeatures()}before creating features.
704      */
readyForFeatureCreation()705     public void readyForFeatureCreation() {
706     }
707 
708     /**
709      * The framework has enabled IMS for the subscription specified, the ImsService should register
710      * for IMS and perform all appropriate initialization to bring up all ImsFeatures.
711      *
712      * @param slotId The slot ID that IMS will be enabled for.
713      * @param subscriptionId The subscription ID that IMS will be enabled for.
714      */
enableImsForSubscription(int slotId, int subscriptionId)715     public void enableImsForSubscription(int slotId, int subscriptionId) {
716         enableIms(slotId);
717     }
718 
719     /**
720      * The framework has disabled IMS for the subscription specified. The ImsService must deregister
721      * for IMS and set capability status to false for all ImsFeatures.
722      * @param slotId The slot ID that IMS will be disabled for.
723      * @param subscriptionId The subscription ID that IMS will be disabled for.
724      */
disableImsForSubscription(int slotId, int subscriptionId)725     public void disableImsForSubscription(int slotId, int subscriptionId) {
726         disableIms(slotId);
727     }
728 
729     /**
730      * The subscription has removed. The ImsService should notify ImsRegistrationImplBase and
731      * ImsConfigImplBase the SIM state was changed.
732      * @param slotId The slot ID which has removed.
733      */
notifySubscriptionRemoved(int slotId)734     private void notifySubscriptionRemoved(int slotId) {
735         ImsRegistrationImplBase registrationImplBase =
736                 getRegistration(slotId);
737         if (registrationImplBase != null) {
738             registrationImplBase.clearRegistrationCache();
739         }
740 
741         ImsConfigImplBase imsConfigImplBase = getConfig(slotId);
742         if (imsConfigImplBase != null) {
743             imsConfigImplBase.clearConfigurationCache();
744         }
745     }
746 
747     /**
748      * The framework has enabled IMS for the slot specified, the ImsService should register for IMS
749      * and perform all appropriate initialization to bring up all ImsFeatures.
750      * @deprecated Use {@link #enableImsForSubscription} instead.
751      */
752     @Deprecated
enableIms(int slotId)753     public void enableIms(int slotId) {
754     }
755 
756     /**
757      * The framework has disabled IMS for the slot specified. The ImsService must deregister for IMS
758      * and set capability status to false for all ImsFeatures.
759      * @deprecated Use {@link #disableImsForSubscription} instead.
760      */
761     @Deprecated
disableIms(int slotId)762     public void disableIms(int slotId) {
763     }
764 
765     /**
766      * The framework has reset IMS for the slot specified. The ImsService must deregister
767      * and release all resources for IMS. After resetIms is called, either
768      * {@link #enableImsForSubscription(int, int)} or {@link #disableImsForSubscription(int, int)}
769      * will be called for the same slotId.
770      *
771      * @param slotId The slot ID that IMS will be reset for.
772      * @hide
773      */
resetIms(int slotId)774     public void resetIms(int slotId) {
775         throw new UnsupportedOperationException();
776     }
777 
778     /**
779      * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
780      * specified subscription.
781      *
782      * @param subscriptionId The subscription ID that the MMTEL Feature is being created for.
783      * @return The newly created {@link MmTelFeature} associated with the subscription or null if
784      * the feature is not supported.
785      */
createMmTelFeatureForSubscription(int slotId, int subscriptionId)786     public @Nullable MmTelFeature createMmTelFeatureForSubscription(int slotId,
787             int subscriptionId) {
788         setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_MMTEL, true);
789         return createMmTelFeature(slotId);
790     }
791 
792     /**
793      * When called, the framework is requesting that a new {@link RcsFeature} is created for the
794      * specified subscription.
795      *
796      * @param subscriptionId The subscription ID that the RCS Feature is being created for.
797      * @return The newly created {@link RcsFeature} associated with the subscription or null if the
798      * feature is not supported.
799      */
createRcsFeatureForSubscription(int slotId, int subscriptionId)800     public @Nullable RcsFeature createRcsFeatureForSubscription(int slotId, int subscriptionId) {
801         setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_RCS, true);
802         return createRcsFeature(slotId);
803     }
804 
805     /**
806      * When called, the framework is requesting that a new emergency-only {@link MmTelFeature} is
807      * created for the specified slot. For emergency calls, there is no known Subscription Id.
808      *
809      * @param slotId The slot ID that the MMTEL Feature is being created for.
810      * @return An MmTelFeature instance to be used for the slot ID when there is not
811      * subscription inserted. Only requested when there is no subscription active on
812      * the specified slot.
813      */
createEmergencyOnlyMmTelFeature(int slotId)814     public @Nullable MmTelFeature createEmergencyOnlyMmTelFeature(int slotId) {
815         setImsFeatureCreatedForSlot(slotId, ImsFeature.FEATURE_MMTEL, true);
816         return createMmTelFeature(slotId);
817     }
818 
819     /**
820      * When called, the framework is requesting that a new {@link MmTelFeature} is created for the
821      * specified slot.
822      * @deprecated Use {@link #createMmTelFeatureForSubscription} instead
823      *
824      * @param slotId The slot ID that the MMTEL Feature is being created for.
825      * @return The newly created {@link MmTelFeature} associated with the slot or null if the
826      * feature is not supported.
827      */
828     @Deprecated
createMmTelFeature(int slotId)829     public MmTelFeature createMmTelFeature(int slotId) {
830         return null;
831     }
832 
833     /**
834      * When called, the framework is requesting that a new {@link RcsFeature} is created for the
835      * specified slot.
836      * @deprecated Use {@link #createRcsFeatureForSubscription} instead
837      *
838      * @param slotId The slot ID that the RCS Feature is being created for.
839      * @return The newly created {@link RcsFeature} associated with the slot or null if the feature
840      * is not supported.
841      */
842     @Deprecated
createRcsFeature(int slotId)843     public RcsFeature createRcsFeature(int slotId) {
844         return null;
845     }
846 
847     /**
848      * Return the {@link ImsConfigImplBase} implementation associated with the provided
849      * subscription. This will be used by the platform to get/set specific IMS related
850      * configurations.
851      *
852      * @param subscriptionId The subscription ID that the IMS configuration is associated with.
853      * @return ImsConfig implementation that is associated with the specified subscription.
854      */
getConfigForSubscription(int slotId, int subscriptionId)855     public @NonNull ImsConfigImplBase getConfigForSubscription(int slotId, int subscriptionId) {
856         return getConfig(slotId);
857     }
858 
859     /**
860      * Return the {@link ImsRegistrationImplBase} implementation associated with the provided
861      * subscription.
862      *
863      * @param subscriptionId The subscription ID that is associated with the IMS Registration.
864      * @return the ImsRegistration implementation associated with the subscription.
865      */
getRegistrationForSubscription(int slotId, int subscriptionId)866     public @NonNull ImsRegistrationImplBase getRegistrationForSubscription(int slotId,
867             int subscriptionId) {
868         return getRegistration(slotId);
869     }
870 
871     /**
872      * Return the {@link ImsConfigImplBase} implementation associated with the provided slot. This
873      * will be used by the platform to get/set specific IMS related configurations.
874      * @deprecated use {@link #getConfigForSubscription} instead.
875      *
876      * @param slotId The slot that the IMS configuration is associated with.
877      * @return ImsConfig implementation that is associated with the specified slot.
878      */
879     @Deprecated
getConfig(int slotId)880     public ImsConfigImplBase getConfig(int slotId) {
881         return new ImsConfigImplBase();
882     }
883 
884     /**
885      * Return the {@link ImsRegistrationImplBase} implementation associated with the provided slot.
886      * @deprecated use  {@link #getRegistrationForSubscription} instead.
887      *
888      * @param slotId The slot that is associated with the IMS Registration.
889      * @return the ImsRegistration implementation associated with the slot.
890      */
891     @Deprecated
getRegistration(int slotId)892     public ImsRegistrationImplBase getRegistration(int slotId) {
893         return new ImsRegistrationImplBase();
894     }
895 
896     /**
897      * Return the {@link SipTransportImplBase} implementation associated with the provided slot.
898      * <p>
899      * This is an optional interface used for devices that must support IMS single registration and
900      * proxy SIP traffic to remote IMS applications. If this is not supported for this IMS service,
901      * this method should return {@code null}. If this feature is supported, then this method must
902      * never be {@code null} and the optional ImsService capability flag
903      * {@link #CAPABILITY_SIP_DELEGATE_CREATION} must be set in
904      * {@link #getImsServiceCapabilities()}. Otherwise the framework will assume this feature is not
905      * supported for this ImsService.
906      * @param slotId The slot that is associated with the SipTransport implementation.
907      * @return the SipTransport implementation for the specified slot.
908      */
909     // ImsService follows a different convention, since it is a stub class. The on* methods are
910     // final and call back into the framework with a state update.
911     @SuppressLint("OnNameExpected")
getSipTransport(int slotId)912     public @Nullable SipTransportImplBase getSipTransport(int slotId) {
913         // Stub implementation for ImsServices that do not support SipTransport.
914         return null;
915     }
916 
sanitizeCapabilities(@msServiceCapability long caps)917     private static long sanitizeCapabilities(@ImsServiceCapability long caps) {
918         long filter = 0xFFFFFFFFFFFFFFFFL;
919         // pad the "allowed" set with zeros
920         filter <<= CAPABILITY_MAX_INDEX + 1;
921         // remove values above the allowed set.
922         caps &= ~filter;
923         // CAPABILITY_EMERGENCY_OVER_MMTEL should also not be set here, will be set by telephony
924         // internally.
925         caps &= ~CAPABILITY_EMERGENCY_OVER_MMTEL;
926         return caps;
927     }
928 
929     /**
930      * @return A string representation of the ImsService capabilities for logging.
931      * @hide
932      */
getCapabilitiesString(@msServiceCapability long caps)933     public static String getCapabilitiesString(@ImsServiceCapability long caps) {
934         StringBuffer result = new StringBuffer();
935         result.append("capabilities={ ");
936         // filter incrementally fills 0s from  left to right. This is used to keep filtering out
937         // more bits in the long until the remaining leftmost bits are all zero.
938         long filter = 0xFFFFFFFFFFFFFFFFL;
939         // position of iterator to potentially print capability.
940         long i = 0;
941         while ((caps & filter) != 0 && i <= 63) {
942             long bitToCheck = (1L << i);
943             if ((caps & bitToCheck) != 0) {
944                 result.append(CAPABILITIES_LOG_MAP.getOrDefault(bitToCheck, bitToCheck + "?"));
945                 result.append(" ");
946             }
947             // shift left by one and fill in another 1 on the leftmost bit.
948             filter <<= 1;
949             i++;
950         }
951         result.append("}");
952         return result.toString();
953     }
954 
955     /**
956      * The ImsService will now be able to define an Executor that the ImsService can be used to
957      * execute the methods. By default all ImsService level method calls will use this Executor.
958      * The ImsService has set the default executor as Runnable::run,
959      * Should be override or default executor will be used.
960      *  @return an Executor used to execute methods called remotely by the framework.
961      */
getExecutor()962     public @NonNull Executor getExecutor() {
963         return Runnable::run;
964     }
965 }
966