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