1 /*
2  * Copyright (C) 2018 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 
20 import android.Manifest;
21 import android.annotation.CallbackExecutor;
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.annotation.RequiresFeature;
25 import android.annotation.RequiresPermission;
26 import android.annotation.SuppressAutoDoc;
27 import android.annotation.SuppressLint;
28 import android.annotation.SystemApi;
29 import android.content.Context;
30 import android.content.pm.PackageManager;
31 import android.os.Binder;
32 import android.os.RemoteException;
33 import android.os.ServiceSpecificException;
34 import android.telephony.AccessNetworkConstants;
35 import android.telephony.BinderCacheManager;
36 import android.telephony.CarrierConfigManager;
37 import android.telephony.SubscriptionManager;
38 import android.telephony.TelephonyFrameworkInitializer;
39 import android.telephony.ims.aidl.IImsCapabilityCallback;
40 import android.telephony.ims.feature.ImsFeature;
41 import android.telephony.ims.feature.MmTelFeature;
42 import android.telephony.ims.stub.ImsRegistrationImplBase;
43 import android.util.Log;
44 
45 import com.android.internal.annotations.VisibleForTesting;
46 import com.android.internal.telephony.IIntegerConsumer;
47 import com.android.internal.telephony.ITelephony;
48 
49 import java.lang.annotation.Retention;
50 import java.lang.annotation.RetentionPolicy;
51 import java.util.Objects;
52 import java.util.concurrent.Executor;
53 import java.util.function.Consumer;
54 
55 /**
56  * A manager for the MmTel (Multimedia Telephony) feature of an IMS network, given an associated
57  * subscription.
58  *
59  * Allows a user to query the IMS MmTel feature information for a subscription, register for
60  * registration and MmTel capability status callbacks, as well as query/modify user settings for the
61  * associated subscription.
62  *
63  * Use {@link android.telephony.ims.ImsManager#getImsMmTelManager(int)} to get an instance of this
64  * manager.
65  */
66 @RequiresFeature(PackageManager.FEATURE_TELEPHONY_IMS)
67 public class ImsMmTelManager implements RegistrationManager {
68     private static final String TAG = "ImsMmTelManager";
69 
70     /**
71      * @hide
72      */
73     @Retention(RetentionPolicy.SOURCE)
74     @IntDef(prefix = "WIFI_MODE_", value = {
75             WIFI_MODE_UNKNOWN,
76             WIFI_MODE_WIFI_ONLY,
77             WIFI_MODE_CELLULAR_PREFERRED,
78             WIFI_MODE_WIFI_PREFERRED
79             })
80     public @interface WiFiCallingMode {}
81 
82     /**
83      * Wifi calling mode is unknown. This is for initialization only.
84      * @hide
85      */
86     public static final int WIFI_MODE_UNKNOWN = -1;
87 
88     /**
89      * Register for IMS over IWLAN if WiFi signal quality is high enough. Do not hand over to LTE
90      * registration if signal quality degrades.
91      */
92     public static final int WIFI_MODE_WIFI_ONLY = 0;
93 
94     /**
95      * Prefer registering for IMS over LTE if LTE signal quality is high enough.
96      */
97     public static final int WIFI_MODE_CELLULAR_PREFERRED = 1;
98 
99     /**
100      * Prefer registering for IMS over IWLAN if possible if WiFi signal quality is high enough.
101      */
102     public static final int WIFI_MODE_WIFI_PREFERRED = 2;
103 
104     /**
105      * Callback class for receiving IMS network Registration callback events.
106      * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
107      * @see #unregisterImsRegistrationCallback(RegistrationCallback)
108      * @deprecated Use {@link RegistrationManager.RegistrationCallback} instead.
109      * @hide
110      */
111     // Do not add to this class, add to RegistrationManager.RegistrationCallback instead.
112     @Deprecated
113     @SystemApi
114     public static class RegistrationCallback extends RegistrationManager.RegistrationCallback {
115 
116         /**
117          * Notifies the framework when the IMS Provider is registered to the IMS network.
118          *
119          * @param imsTransportType the radio access technology.
120          */
121         @Override
onRegistered(@ccessNetworkConstants.TransportType int imsTransportType)122         public void onRegistered(@AccessNetworkConstants.TransportType int imsTransportType) {
123         }
124 
125         /**
126          * Notifies the framework when the IMS Provider is trying to register the IMS network.
127          *
128          * @param imsTransportType the radio access technology.
129          */
130         @Override
onRegistering(@ccessNetworkConstants.TransportType int imsTransportType)131         public void onRegistering(@AccessNetworkConstants.TransportType int imsTransportType) {
132         }
133 
134         /**
135          * Notifies the framework when the IMS Provider is deregistered from the IMS network.
136          *
137          * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
138          */
139         @Override
onUnregistered(@onNull ImsReasonInfo info)140         public void onUnregistered(@NonNull ImsReasonInfo info) {
141         }
142 
143         /**
144          * A failure has occurred when trying to handover registration to another technology type.
145          *
146          * @param imsTransportType The transport type that has failed to handover registration to.
147          * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
148          */
149         @Override
onTechnologyChangeFailed( @ccessNetworkConstants.TransportType int imsTransportType, @NonNull ImsReasonInfo info)150         public void onTechnologyChangeFailed(
151                 @AccessNetworkConstants.TransportType int imsTransportType,
152                 @NonNull ImsReasonInfo info) {
153         }
154     }
155 
156     /**
157      * Receives IMS capability status updates from the ImsService.
158      *
159      * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
160      * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
161      */
162     public static class CapabilityCallback {
163 
164         private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
165 
166             private final CapabilityCallback mLocalCallback;
167             private Executor mExecutor;
168 
CapabilityBinder(CapabilityCallback c)169             CapabilityBinder(CapabilityCallback c) {
170                 mLocalCallback = c;
171             }
172 
173             @Override
onCapabilitiesStatusChanged(int config)174             public void onCapabilitiesStatusChanged(int config) {
175                 if (mLocalCallback == null) return;
176 
177                 final long callingIdentity = Binder.clearCallingIdentity();
178                 try {
179                     mExecutor.execute(() -> mLocalCallback.onCapabilitiesStatusChanged(
180                             new MmTelFeature.MmTelCapabilities(config)));
181                 } finally {
182                     restoreCallingIdentity(callingIdentity);
183                 }
184             }
185 
186             @Override
onQueryCapabilityConfiguration(int capability, int radioTech, boolean isEnabled)187             public void onQueryCapabilityConfiguration(int capability, int radioTech,
188                     boolean isEnabled) {
189                 // This is not used for public interfaces.
190             }
191 
192             @Override
onChangeCapabilityConfigurationError(int capability, int radioTech, @ImsFeature.ImsCapabilityError int reason)193             public void onChangeCapabilityConfigurationError(int capability, int radioTech,
194                     @ImsFeature.ImsCapabilityError int reason) {
195                 // This is not used for public interfaces
196             }
197 
setExecutor(Executor executor)198             private void setExecutor(Executor executor) {
199                 mExecutor = executor;
200             }
201         }
202 
203         private final CapabilityBinder mBinder = new CapabilityBinder(this);
204 
205         /**
206          * The status of the feature's capabilities has changed to either available or unavailable.
207          * If unavailable, the feature is not able to support the unavailable capability at this
208          * time.
209          *
210          * @param capabilities The new availability of the capabilities.
211          */
onCapabilitiesStatusChanged( @onNull MmTelFeature.MmTelCapabilities capabilities)212         public void onCapabilitiesStatusChanged(
213                 @NonNull MmTelFeature.MmTelCapabilities capabilities) {
214         }
215 
216         /**@hide*/
getBinder()217         public final IImsCapabilityCallback getBinder() {
218             return mBinder;
219         }
220 
221         /**@hide*/
222         // Only exposed as public method for compatibility with deprecated ImsManager APIs.
223         // TODO: clean up dependencies and change back to private visibility.
setExecutor(Executor executor)224         public final void setExecutor(Executor executor) {
225             mBinder.setExecutor(executor);
226         }
227     }
228 
229     private final Context mContext;
230     private final int mSubId;
231     private final BinderCacheManager<ITelephony> mBinderCache;
232 
233     // Cache Telephony Binder interfaces, one cache per process.
234     private static final BinderCacheManager<ITelephony> sTelephonyCache =
235             new BinderCacheManager<>(ImsMmTelManager::getITelephonyInterface);
236 
237     /**
238      * Create an instance of {@link ImsMmTelManager} for the subscription id specified.
239      *
240      * @param subId The ID of the subscription that this ImsMmTelManager will use.
241      * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
242      *
243      * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
244      * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
245      * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
246      *
247      * @throws IllegalArgumentException if the subscription is invalid.
248      * @deprecated Use {@link android.telephony.ims.ImsManager#getImsMmTelManager(int)} to get an
249      * instance of this class.
250      * @hide
251      */
252     @SystemApi
253     @Deprecated
254     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
255     @RequiresPermission(anyOf = {
256             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
257             android.Manifest.permission.READ_PRECISE_PHONE_STATE
258     })
259     @SuppressLint("ManagerLookup")
createForSubscriptionId(int subId)260     public static @NonNull ImsMmTelManager createForSubscriptionId(int subId) {
261         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
262             throw new IllegalArgumentException("Invalid subscription ID");
263         }
264 
265         return new ImsMmTelManager(subId, sTelephonyCache);
266     }
267 
268     /**
269      * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead.
270      * @hide
271      */
272     @VisibleForTesting
ImsMmTelManager(int subId, BinderCacheManager<ITelephony> binderCache)273     public ImsMmTelManager(int subId, BinderCacheManager<ITelephony> binderCache) {
274         this(null, subId, binderCache);
275     }
276 
277     /**
278      * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead.
279      * @hide
280      */
281     @VisibleForTesting
ImsMmTelManager(Context context, int subId, BinderCacheManager<ITelephony> binderCache)282     public ImsMmTelManager(Context context, int subId, BinderCacheManager<ITelephony> binderCache) {
283         mContext = context;
284         mSubId = subId;
285         mBinderCache = binderCache;
286     }
287 
288     /**
289      * Registers a {@link RegistrationCallback} with the system, which will provide registration
290      * updates for the subscription specified in {@link ImsManager#getImsMmTelManager(int)}. Use
291      * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
292      * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
293      *
294      * When the callback is registered, it will initiate the callback c to be called with the
295      * current registration state.
296      *
297      * @param executor The executor the callback events should be run on.
298      * @param c The {@link RegistrationCallback} to be added.
299      * @see #unregisterImsRegistrationCallback(RegistrationCallback)
300      * @throws IllegalArgumentException if the subscription associated with this callback is not
301      * active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or
302      * {@link CapabilityCallback} callback.
303      * @throws ImsException if the subscription associated with this callback is valid, but
304      * the {@link ImsService} associated with the subscription is not available. This can happen if
305      * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
306      * reason.
307      * @deprecated Use {@link RegistrationManager#registerImsRegistrationCallback(Executor,
308      * RegistrationManager.RegistrationCallback)} instead.
309      * @hide
310      */
311     @Deprecated
312     @SystemApi
313     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
registerImsRegistrationCallback(@onNull @allbackExecutor Executor executor, @NonNull RegistrationCallback c)314     public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor,
315             @NonNull RegistrationCallback c) throws ImsException {
316         if (c == null) {
317             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
318         }
319         if (executor == null) {
320             throw new IllegalArgumentException("Must include a non-null Executor.");
321         }
322         c.setExecutor(executor);
323 
324         ITelephony iTelephony = getITelephony();
325         if (iTelephony == null) {
326             throw new ImsException("Could not find Telephony Service.",
327                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
328         }
329 
330         try {
331             iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder());
332         } catch (ServiceSpecificException e) {
333             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
334                 // Rethrow as runtime error to keep API compatible.
335                 throw new IllegalArgumentException(e.getMessage());
336             } else {
337                 throw new ImsException(e.getMessage(), e.errorCode);
338             }
339         } catch (RemoteException | IllegalStateException e) {
340             throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
341         }
342     }
343 
344      /**
345      *
346      * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
347      * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
348      * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
349      *
350      * {@inheritDoc}
351      *
352      */
353     @Override
354     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
355     @RequiresPermission(anyOf = {
356             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
357             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
registerImsRegistrationCallback(@onNull @allbackExecutor Executor executor, @NonNull RegistrationManager.RegistrationCallback c)358     public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor,
359             @NonNull RegistrationManager.RegistrationCallback c) throws ImsException {
360         if (c == null) {
361             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
362         }
363         if (executor == null) {
364             throw new IllegalArgumentException("Must include a non-null Executor.");
365         }
366         c.setExecutor(executor);
367 
368         ITelephony iTelephony = getITelephony();
369         if (iTelephony == null) {
370             throw new ImsException("Could not find Telephony Service.",
371                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
372         }
373 
374         try {
375             iTelephony.registerImsRegistrationCallback(mSubId, c.getBinder());
376         } catch (ServiceSpecificException e) {
377             throw new ImsException(e.getMessage(), e.errorCode);
378         } catch (RemoteException | IllegalStateException e) {
379             throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
380         }
381     }
382 
383     /**
384      * Removes an existing {@link RegistrationCallback}.
385      *
386      * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
387      * etc...), this callback will automatically be removed. If this method is called for an
388      * inactive subscription, it will result in a no-op.
389      *
390      * @param c The {@link RegistrationCallback} to be removed.
391      * @see SubscriptionManager.OnSubscriptionsChangedListener
392      * @see #registerImsRegistrationCallback(Executor, RegistrationCallback)
393      * @deprecated Use {@link #unregisterImsRegistrationCallback(
394      * RegistrationManager.RegistrationCallback)}.
395      * @hide
396      */
397     @Deprecated
398     @SystemApi
399     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
unregisterImsRegistrationCallback(@onNull RegistrationCallback c)400     public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
401         if (c == null) {
402             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
403         }
404 
405         ITelephony iTelephony = getITelephony();
406         if (iTelephony == null) {
407             throw new RuntimeException("Could not find Telephony Service.");
408         }
409 
410         try {
411             iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder());
412         } catch (RemoteException e) {
413             throw e.rethrowAsRuntimeException();
414         }
415     }
416 
417      /**
418      *
419      * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
420      * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
421      * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
422      * Access by profile owners is deprecated and will be removed in a future release.
423      *
424      *{@inheritDoc}
425      */
426     @Override
427     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
428     @RequiresPermission(anyOf = {
429             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
430             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
unregisterImsRegistrationCallback( @onNull RegistrationManager.RegistrationCallback c)431     public void unregisterImsRegistrationCallback(
432             @NonNull RegistrationManager.RegistrationCallback c) {
433         if (c == null) {
434             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
435         }
436 
437         ITelephony iTelephony = getITelephony();
438         if (iTelephony == null) {
439             throw new RuntimeException("Could not find Telephony Service.");
440         }
441 
442         try {
443             iTelephony.unregisterImsRegistrationCallback(mSubId, c.getBinder());
444         } catch (RemoteException e) {
445             throw e.rethrowAsRuntimeException();
446         }
447     }
448 
449     /**
450      * {@inheritDoc}
451      * @hide
452      */
453     @Override
454     @SystemApi
455     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
getRegistrationState(@onNull @allbackExecutor Executor executor, @NonNull @ImsRegistrationState Consumer<Integer> stateCallback)456     public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
457             @NonNull @ImsRegistrationState Consumer<Integer> stateCallback) {
458         if (stateCallback == null) {
459             throw new IllegalArgumentException("Must include a non-null callback.");
460         }
461         if (executor == null) {
462             throw new IllegalArgumentException("Must include a non-null Executor.");
463         }
464 
465         ITelephony iTelephony = getITelephony();
466         if (iTelephony == null) {
467             throw new RuntimeException("Could not find Telephony Service.");
468         }
469 
470         try {
471             iTelephony.getImsMmTelRegistrationState(mSubId, new IIntegerConsumer.Stub() {
472                 @Override
473                 public void accept(int result) {
474                     final long identity = Binder.clearCallingIdentity();
475                     try {
476                         executor.execute(() -> stateCallback.accept(result));
477                     } finally {
478                         Binder.restoreCallingIdentity(identity);
479                     }
480                 }
481             });
482         } catch (ServiceSpecificException | RemoteException e) {
483             Log.w("ImsMmTelManager", "Error getting registration state: " + e);
484             executor.execute(() -> stateCallback.accept(REGISTRATION_STATE_NOT_REGISTERED));
485         }
486     }
487 
488     /**
489      * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE
490      * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges
491      * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
492      * Access by profile owners is deprecated and will be removed in a future release.
493      *
494      *{@inheritDoc}
495      */
496     @Override
497     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
498     @RequiresPermission(anyOf = {
499             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
500             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
getRegistrationTransportType(@onNull @allbackExecutor Executor executor, @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback)501     public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
502             @NonNull @AccessNetworkConstants.TransportType
503                     Consumer<Integer> transportTypeCallback) {
504         if (transportTypeCallback == null) {
505             throw new IllegalArgumentException("Must include a non-null callback.");
506         }
507         if (executor == null) {
508             throw new IllegalArgumentException("Must include a non-null Executor.");
509         }
510 
511         ITelephony iTelephony = getITelephony();
512         if (iTelephony == null) {
513             throw new RuntimeException("Could not find Telephony Service.");
514         }
515 
516         try {
517             iTelephony.getImsMmTelRegistrationTransportType(mSubId,
518                     new IIntegerConsumer.Stub() {
519                         @Override
520                         public void accept(int result) {
521                             final long identity = Binder.clearCallingIdentity();
522                             try {
523                                 executor.execute(() -> transportTypeCallback.accept(result));
524                             } finally {
525                                 Binder.restoreCallingIdentity(identity);
526                             }
527                         }
528                     });
529         } catch (ServiceSpecificException | RemoteException e) {
530             Log.w("ImsMmTelManager", "Error getting transport type: " + e);
531             executor.execute(() -> transportTypeCallback.accept(
532                     AccessNetworkConstants.TRANSPORT_TYPE_INVALID));
533         }
534     }
535 
536     /**
537      * Registers a {@link CapabilityCallback} with the system, which will provide MmTel service
538      * availability updates for the subscription specified in
539      * {@link ImsManager#getImsMmTelManager(int)}.
540      *
541      * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
542      * subscription changed events and call
543      * {@link #unregisterMmTelCapabilityCallback(CapabilityCallback)} to clean up.
544      * <p>This API requires one of the following:
545      * <ul>
546      *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
547      *     <li>If the caller is the device or profile owner, the caller holds the
548      *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
549      *     <li>The caller has carrier privileges (see
550      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
551      *     active subscription.</li>
552      * </ul>
553      * <p>The profile owner is an app that owns a managed profile on the device; for more details
554      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
555      * Access by profile owners is deprecated and will be removed in a future release.
556      *
557      * When the callback is registered, it will initiate the callback c to be called with the
558      * current capabilities.
559      *
560      * @param executor The executor the callback events should be run on.
561      * @param c The MmTel {@link CapabilityCallback} to be registered.
562      * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
563      * @throws ImsException if the subscription associated with this callback is valid, but
564      * the {@link ImsService} associated with the subscription is not available. This can happen if
565      * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
566      * reason.
567      */
568     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
569     @RequiresPermission(anyOf = {
570             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
571             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
registerMmTelCapabilityCallback(@onNull @allbackExecutor Executor executor, @NonNull CapabilityCallback c)572     public void registerMmTelCapabilityCallback(@NonNull @CallbackExecutor Executor executor,
573             @NonNull CapabilityCallback c) throws ImsException {
574         if (c == null) {
575             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
576         }
577         if (executor == null) {
578             throw new IllegalArgumentException("Must include a non-null Executor.");
579         }
580         c.setExecutor(executor);
581 
582         ITelephony iTelephony = getITelephony();
583         if (iTelephony == null) {
584             throw new ImsException("Could not find Telephony Service.",
585                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
586         }
587 
588         try {
589             iTelephony.registerMmTelCapabilityCallback(mSubId, c.getBinder());
590         } catch (ServiceSpecificException e) {
591             throw new ImsException(e.getMessage(), e.errorCode);
592         } catch (RemoteException e) {
593             throw e.rethrowAsRuntimeException();
594         }  catch (IllegalStateException e) {
595             throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
596         }
597     }
598 
599     /**
600      * Removes an existing MmTel {@link CapabilityCallback}.
601      *
602      * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
603      * etc...), this callback will automatically be removed. If this method is called for an
604      * inactive subscription, it will result in a no-op.
605      * <p>This API requires one of the following:
606      * <ul>
607      *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
608      *     <li>If the caller is the device or profile owner, the caller holds the
609      *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
610      *     <li>The caller has carrier privileges (see
611      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
612      *     active subscription.</li>
613      * </ul>
614      * <p>The profile owner is an app that owns a managed profile on the device; for more details
615      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
616      * Access by profile owners is deprecated and will be removed in a future release.
617      *
618      * @param c The MmTel {@link CapabilityCallback} to be removed.
619      * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback)
620      */
621     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
622     @RequiresPermission(anyOf = {
623             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
624             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
unregisterMmTelCapabilityCallback(@onNull CapabilityCallback c)625     public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
626         if (c == null) {
627             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
628         }
629 
630         ITelephony iTelephony = getITelephony();
631         if (iTelephony == null) {
632             Log.w("ImsMmTelManager", "Could not find Telephony Service.");
633             return;
634         }
635         try {
636             iTelephony.unregisterMmTelCapabilityCallback(mSubId, c.getBinder());
637         } catch (RemoteException e) {
638             throw e.rethrowAsRuntimeException();
639         }
640     }
641 
642     /**
643      * Query the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
644      * enable MmTel IMS features, depending on the carrier configuration for the current
645      * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
646      * be enabled as long as the carrier has provisioned these services for the specified
647      * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
648      * carrier requirements.
649      * <p>
650      * Note: If the carrier configuration for advanced calling is not editable or hidden, this
651      * method will always return the default value.
652      * <p>This API requires one of the following:
653      * <ul>
654      *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
655      *     <li>If the caller is the device or profile owner, the caller holds the
656      *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
657      *     <li>The caller has carrier privileges (see
658      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
659      *     active subscription.</li>
660      * </ul>
661      * <p>The profile owner is an app that owns a managed profile on the device; for more details
662      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
663      * Access by profile owners is deprecated and will be removed in a future release.
664      *
665      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
666      * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
667      * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
668      * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
669      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
670      * @throws IllegalArgumentException if the subscription associated with this operation is not
671      * active (SIM is not inserted, ESIM inactive) or invalid.
672      * @return true if the user's setting for advanced calling is enabled, false otherwise.
673      */
674     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
675     @RequiresPermission(anyOf = {
676             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
677             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
isAdvancedCallingSettingEnabled()678     public boolean isAdvancedCallingSettingEnabled() {
679         ITelephony iTelephony = getITelephony();
680         if (iTelephony == null) {
681             throw new RuntimeException("Could not find Telephony Service.");
682         }
683 
684         try {
685             return iTelephony.isAdvancedCallingSettingEnabled(mSubId);
686         } catch (ServiceSpecificException e) {
687             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
688                 // Rethrow as runtime error to keep API compatible.
689                 throw new IllegalArgumentException(e.getMessage());
690             } else {
691                 throw new RuntimeException(e.getMessage());
692             }
693         } catch (RemoteException e) {
694             throw e.rethrowAsRuntimeException();
695         }
696     }
697 
698     /**
699      * Modify the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
700      * enable MmTel IMS features, depending on the carrier configuration for the current
701      * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
702      * be enabled as long as the carrier has provisioned these services for the specified
703      * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
704      * carrier requirements.
705      *
706      * Modifying this value may also trigger an IMS registration or deregistration, depending on
707      * whether or not the new value is enabled or disabled.
708      *
709      * Note: If the carrier configuration for advanced calling is not editable or hidden, this
710      * method will do nothing and will instead always use the default value.
711      *
712      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
713      * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
714      * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
715      * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
716      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
717      * @see #isAdvancedCallingSettingEnabled()
718      * @throws IllegalArgumentException if the subscription associated with this operation is not
719      * active (SIM is not inserted, ESIM inactive) or invalid.
720      * @hide
721      */
722     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
723     @SystemApi
setAdvancedCallingSettingEnabled(boolean isEnabled)724     public void setAdvancedCallingSettingEnabled(boolean isEnabled) {
725         ITelephony iTelephony = getITelephony();
726         if (iTelephony == null) {
727             throw new RuntimeException("Could not find Telephony Service.");
728         }
729 
730         try {
731             iTelephony.setAdvancedCallingSettingEnabled(mSubId, isEnabled);
732         } catch (ServiceSpecificException e) {
733             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
734                 // Rethrow as runtime error to keep API compatible.
735                 throw new IllegalArgumentException(e.getMessage());
736             } else {
737                 throw new RuntimeException(e.getMessage());
738             }
739         } catch (RemoteException e) {
740             throw e.rethrowAsRuntimeException();
741         }
742     }
743 
744     /**
745      * Query the IMS MmTel capability for a given registration technology. This does not
746      * necessarily mean that we are registered and the capability is available, but rather the
747      * subscription is capable of this service over IMS.
748      *
749      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
750      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VT_AVAILABLE_BOOL
751      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_IMS_GBA_REQUIRED_BOOL
752      * @see #isAvailable(int, int)
753      *
754      * @param imsRegTech The IMS registration technology.
755      * @param capability The IMS MmTel capability to query.
756      * @return {@code true} if the MmTel IMS capability is capable for this subscription, false
757      *         otherwise.
758      * @hide
759      */
760     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
761     @SystemApi
isCapable(@mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech)762     public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
763             @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
764         ITelephony iTelephony = getITelephony();
765         if (iTelephony == null) {
766             throw new RuntimeException("Could not find Telephony Service.");
767         }
768 
769         try {
770             return iTelephony.isCapable(mSubId, capability, imsRegTech);
771         } catch (RemoteException e) {
772             throw e.rethrowAsRuntimeException();
773         }
774     }
775 
776     /**
777      * Query the availability of an IMS MmTel capability for a given registration technology. If
778      * a capability is available, IMS is registered and the service is currently available over IMS.
779      *
780      * @see #isCapable(int, int)
781      *
782      * @param imsRegTech The IMS registration technology.
783      * @param capability The IMS MmTel capability to query.
784      * @return {@code true} if the MmTel IMS capability is available for this subscription, false
785      *         otherwise.
786      * @hide
787      */
788     @SystemApi
789     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
isAvailable(@mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech)790     public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
791             @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
792         ITelephony iTelephony = getITelephony();
793         if (iTelephony == null) {
794             throw new RuntimeException("Could not find Telephony Service.");
795         }
796 
797         try {
798             return iTelephony.isAvailable(mSubId, capability, imsRegTech);
799         } catch (RemoteException e) {
800             throw e.rethrowAsRuntimeException();
801         }
802     }
803 
804     /**
805      * Query whether or not the requested MmTel capability is supported by the carrier on the
806      * specified network transport.
807      * <p>
808      * This is a configuration option and does not change. The only time this may change is if a
809      * new IMS configuration is loaded when there is a
810      * {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED} broadcast for this subscription.
811      * @param capability The capability that is being queried for support on the carrier network.
812      * @param transportType The transport type of the capability to check support for.
813      * @param executor The executor that the callback will be called with.
814      * @param callback A consumer containing a Boolean result specifying whether or not the
815      *                 capability is supported on this carrier network for the transport specified.
816      * @throws ImsException if the subscription is no longer valid or the IMS service is not
817      * available.
818      * @hide
819      */
820     @SystemApi
821     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
isSupported(@mTelFeature.MmTelCapabilities.MmTelCapability int capability, @AccessNetworkConstants.TransportType int transportType, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback)822     public void isSupported(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
823             @AccessNetworkConstants.TransportType int transportType,
824             @NonNull @CallbackExecutor Executor executor,
825             @NonNull Consumer<Boolean> callback) throws ImsException {
826         if (callback == null) {
827             throw new IllegalArgumentException("Must include a non-null Consumer.");
828         }
829         if (executor == null) {
830             throw new IllegalArgumentException("Must include a non-null Executor.");
831         }
832 
833         ITelephony iTelephony = getITelephony();
834         if (iTelephony == null) {
835             throw new ImsException("Could not find Telephony Service.",
836                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
837         }
838 
839         try {
840             iTelephony.isMmTelCapabilitySupported(mSubId, new IIntegerConsumer.Stub() {
841                 @Override
842                 public void accept(int result) {
843                     final long identity = Binder.clearCallingIdentity();
844                     try {
845                         executor.execute(() -> callback.accept(result == 1));
846                     } finally {
847                         Binder.restoreCallingIdentity(identity);
848                     }
849                 }
850             }, capability, transportType);
851         } catch (ServiceSpecificException sse) {
852             throw new ImsException(sse.getMessage(), sse.errorCode);
853         } catch (RemoteException e) {
854             e.rethrowAsRuntimeException();
855         }
856     }
857 
858     /**
859      * The user's setting for whether or not they have enabled the "Video Calling" setting.
860      *
861      * <p>
862      * Note: If the carrier configuration for advanced calling is not editable or hidden, this
863      * method will always return the default value.
864      * <p>This API requires one of the following:
865      * <ul>
866      *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
867      *     <li>If the caller is the device or profile owner, the caller holds the
868      *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
869      *     <li>The caller has carrier privileges (see
870      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
871      *     active subscription.</li>
872      * </ul>
873      * <p>The profile owner is an app that owns a managed profile on the device; for more details
874      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
875      * Access by profile owners is deprecated and will be removed in a future release.
876      *
877      * @throws IllegalArgumentException if the subscription associated with this operation is not
878      * active (SIM is not inserted, ESIM inactive) or invalid.
879      * @return true if the user’s “Video Calling” setting is currently enabled.
880      */
881     @RequiresPermission(anyOf = {
882             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
883             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
884     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
isVtSettingEnabled()885     public boolean isVtSettingEnabled() {
886         ITelephony iTelephony = getITelephony();
887         if (iTelephony == null) {
888             throw new RuntimeException("Could not find Telephony Service.");
889         }
890 
891         try {
892             return iTelephony.isVtSettingEnabled(mSubId);
893         } catch (ServiceSpecificException e) {
894             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
895                 // Rethrow as runtime error to keep API compatible.
896                 throw new IllegalArgumentException(e.getMessage());
897             } else {
898                 throw new RuntimeException(e.getMessage());
899             }
900         } catch (RemoteException e) {
901             throw e.rethrowAsRuntimeException();
902         }
903     }
904 
905     /**
906      * Change the user's setting for Video Telephony and enable the Video Telephony capability.
907      *
908      * @throws IllegalArgumentException if the subscription associated with this operation is not
909      * active (SIM is not inserted, ESIM inactive) or invalid.
910      * @see #isVtSettingEnabled()
911      * @hide
912      */
913     @SystemApi
914     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setVtSettingEnabled(boolean isEnabled)915     public void setVtSettingEnabled(boolean isEnabled) {
916         ITelephony iTelephony = getITelephony();
917         if (iTelephony == null) {
918             throw new RuntimeException("Could not find Telephony Service.");
919         }
920 
921         try {
922             iTelephony.setVtSettingEnabled(mSubId, isEnabled);
923         } catch (ServiceSpecificException e) {
924             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
925                 // Rethrow as runtime error to keep API compatible.
926                 throw new IllegalArgumentException(e.getMessage());
927             } else {
928                 throw new RuntimeException(e.getMessage());
929             }
930         } catch (RemoteException e) {
931             throw e.rethrowAsRuntimeException();
932         }
933     }
934 
935     /**
936      * @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
937      *
938      * <p>This API requires one of the following:
939      * <ul>
940      *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
941      *     <li>If the caller is the device or profile owner, the caller holds the
942      *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
943      *     <li>The caller has carrier privileges (see
944      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
945      *     active subscription.</li>
946      * </ul>
947      * <p>The profile owner is an app that owns a managed profile on the device; for more details
948      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
949      * Access by profile owners is deprecated and will be removed in a future release.
950      *
951      * @throws IllegalArgumentException if the subscription associated with this operation is not
952      * active (SIM is not inserted, ESIM inactive) or invalid.
953      */
954     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
955     @RequiresPermission(anyOf = {
956             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
957             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
isVoWiFiSettingEnabled()958     public boolean isVoWiFiSettingEnabled() {
959         ITelephony iTelephony = getITelephony();
960         if (iTelephony == null) {
961             throw new RuntimeException("Could not find Telephony Service.");
962         }
963 
964         try {
965             return iTelephony.isVoWiFiSettingEnabled(mSubId);
966         } catch (ServiceSpecificException e) {
967             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
968                 // Rethrow as runtime error to keep API compatible.
969                 throw new IllegalArgumentException(e.getMessage());
970             } else {
971                 throw new RuntimeException(e.getMessage());
972             }
973         } catch (RemoteException e) {
974             throw e.rethrowAsRuntimeException();
975         }
976     }
977 
978     /**
979      * Sets the user's setting for whether or not Voice over WiFi is enabled.
980      *
981      * @throws IllegalArgumentException if the subscription associated with this operation is not
982      * active (SIM is not inserted, ESIM inactive) or invalid.
983      * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
984      * @see #isVoWiFiSettingEnabled()
985      * @hide
986      */
987     @SystemApi
988     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setVoWiFiSettingEnabled(boolean isEnabled)989     public void setVoWiFiSettingEnabled(boolean isEnabled) {
990         ITelephony iTelephony = getITelephony();
991         if (iTelephony == null) {
992             throw new RuntimeException("Could not find Telephony Service.");
993         }
994 
995         try {
996             iTelephony.setVoWiFiSettingEnabled(mSubId, isEnabled);
997         } catch (ServiceSpecificException e) {
998             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
999                 // Rethrow as runtime error to keep API compatible.
1000                 throw new IllegalArgumentException(e.getMessage());
1001             } else {
1002                 throw new RuntimeException(e.getMessage());
1003             }
1004         } catch (RemoteException e) {
1005             throw e.rethrowAsRuntimeException();
1006         }
1007     }
1008 
1009     /**
1010      * This configuration is meaningful only on dual sim device.
1011      * If enabled, this will result in the device setting up IMS of all other
1012      * active subscriptions over the INTERNET APN of the primary default data subscription
1013      * when any of those subscriptions are roaming or out of service and if wifi is not available
1014      * for VoWifi. This feature will be disabled if
1015      * {@link CarrierConfigManager#KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL} is set to false.
1016      * <p>Following are the conditions in which system will try to register IMS over
1017      * cross sim
1018      * <ul>
1019      *     <li>Wifi is not available, one SIM is roaming and the default data
1020      *     SIM is in home network. Then roaming SIM IMS will be registered over INTERNET APN of the
1021      *     default data subscription </li>
1022      *     <li>Wifi is not available, one SIM is out of service and the default data
1023      *     SIM is in home network. Then out of service SIM IMS will be registered over INTERNET
1024      *     APN of the default data subscription </li>
1025      * </ul>
1026      * <p>This API requires one of the following:
1027      * <ul>
1028      *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
1029      *     <li>If the caller is the device or profile owner, the caller holds the
1030      *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
1031      *     <li>The caller has carrier privileges (see
1032      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
1033      *     active subscription.</li>
1034      * </ul>
1035      * <p>The profile owner is an app that owns a managed profile on the device; for more details
1036      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
1037      * Access by profile owners is deprecated and will be removed in a future release.
1038      *
1039      * @throws ImsException if the IMS service associated with this subscription is not available or
1040      * the IMS service is not available.
1041      * @return true if the user's setting for Voice over Cross SIM is enabled and false if it is not
1042      */
1043     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
1044     @RequiresPermission(anyOf = {
1045             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
1046             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
isCrossSimCallingEnabled()1047     public boolean isCrossSimCallingEnabled() throws ImsException {
1048         ITelephony iTelephony = getITelephony();
1049         if (iTelephony == null) {
1050             throw new ImsException("Could not find Telephony Service.",
1051                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
1052         }
1053 
1054         try {
1055             return iTelephony.isCrossSimCallingEnabledByUser(mSubId);
1056         } catch (ServiceSpecificException sse) {
1057             throw new ImsException(sse.getMessage(), sse.errorCode);
1058         } catch (RemoteException e) {
1059             e.rethrowAsRuntimeException();
1060         }
1061         // Not reachable. Adding return to make compiler happy.
1062         return false;
1063     }
1064 
1065     /**
1066      * Sets the user's setting for whether or not Voice over Cross SIM is enabled.
1067      * If enabled, this will result in the device setting up IMS of all other
1068      * active subscriptions over the INTERNET APN of the primary default data subscription
1069      * when any of those subscriptions are roaming or out of service and if wifi is not available
1070      * for VoWifi. This feature will be disabled if
1071      * {@link CarrierConfigManager#KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL} is set to false.
1072      *
1073      * <p>Following are the conditions in which system will try to register IMS over
1074      * cross sim
1075      * <ul>
1076      *     <li>Wifi is not available, one SIM is roaming and the default data
1077      *     SIM is in home network. Then roaming SIM IMS will be registered over INTERNET APN of the
1078      *     default data subscription </li>
1079      *     <li>Wifi is not available, one SIM is out of service and the default data
1080      *     SIM is in home network. Then out of service SIM IMS will be registered over INTERNET
1081      *     APN of the default data subscription </li>
1082      * </ul>
1083      * @throws ImsException if the IMS service associated with this subscription is not available or
1084      * the IMS service is not available.
1085      * @param isEnabled true if the user's setting for Voice over Cross SIM is enabled,
1086      *                 false otherwise
1087      * @see #isCrossSimCallingEnabled()
1088      * @hide
1089      */
1090     @SystemApi
1091     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setCrossSimCallingEnabled(boolean isEnabled)1092     public void setCrossSimCallingEnabled(boolean isEnabled) throws ImsException {
1093         ITelephony iTelephony = getITelephony();
1094         if (iTelephony == null) {
1095             throw new ImsException("Could not find Telephony Service.",
1096                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
1097         }
1098 
1099         try {
1100             iTelephony.setCrossSimCallingEnabled(mSubId, isEnabled);
1101         } catch (ServiceSpecificException sse) {
1102             throw new ImsException(sse.getMessage(), sse.errorCode);
1103         } catch (RemoteException e) {
1104             e.rethrowAsRuntimeException();
1105         }
1106     }
1107 
1108     /**
1109      * Returns the user's voice over WiFi roaming setting associated with the current subscription.
1110      *
1111      * <p>This API requires one of the following:
1112      * <ul>
1113      *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
1114      *     <li>If the caller is the device or profile owner, the caller holds the
1115      *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
1116      *     <li>The caller has carrier privileges (see
1117      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
1118      *     active subscription.</li>
1119      * </ul>
1120      * <p>The profile owner is an app that owns a managed profile on the device; for more details
1121      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
1122      * Access by profile owners is deprecated and will be removed in a future release.
1123      *
1124      * @throws IllegalArgumentException if the subscription associated with this operation is not
1125      * active (SIM is not inserted, ESIM inactive) or invalid.
1126      * @return true if the user's setting for Voice over WiFi while roaming is enabled, false
1127      * if disabled.
1128      */
1129     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
1130     @RequiresPermission(anyOf = {
1131             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
1132             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
isVoWiFiRoamingSettingEnabled()1133     public boolean isVoWiFiRoamingSettingEnabled() {
1134         ITelephony iTelephony = getITelephony();
1135         if (iTelephony == null) {
1136             throw new RuntimeException("Could not find Telephony Service.");
1137         }
1138 
1139         try {
1140             return iTelephony.isVoWiFiRoamingSettingEnabled(mSubId);
1141         } catch (ServiceSpecificException e) {
1142             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
1143                 // Rethrow as runtime error to keep API compatible.
1144                 throw new IllegalArgumentException(e.getMessage());
1145             } else {
1146                 throw new RuntimeException(e.getMessage());
1147             }
1148         } catch (RemoteException e) {
1149             throw e.rethrowAsRuntimeException();
1150         }
1151     }
1152 
1153     /**
1154      * Change the user's setting for Voice over WiFi while roaming.
1155      *
1156      * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled,
1157      *     false otherwise.
1158      * @throws IllegalArgumentException if the subscription associated with this operation is not
1159      * active (SIM is not inserted, ESIM inactive) or invalid.
1160      * @see #isVoWiFiRoamingSettingEnabled()
1161      * @hide
1162      */
1163     @SystemApi
1164     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setVoWiFiRoamingSettingEnabled(boolean isEnabled)1165     public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) {
1166         ITelephony iTelephony = getITelephony();
1167         if (iTelephony == null) {
1168             throw new RuntimeException("Could not find Telephony Service.");
1169         }
1170 
1171         try {
1172             iTelephony.setVoWiFiRoamingSettingEnabled(mSubId, isEnabled);
1173         } catch (ServiceSpecificException e) {
1174             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
1175                 // Rethrow as runtime error to keep API compatible.
1176                 throw new IllegalArgumentException(e.getMessage());
1177             } else {
1178                 throw new RuntimeException(e.getMessage());
1179             }
1180         } catch (RemoteException e) {
1181             throw e.rethrowAsRuntimeException();
1182         }
1183     }
1184 
1185     /**
1186      * Overrides the Voice over WiFi capability to true for IMS, but do not persist the setting.
1187      * Typically used during the Voice over WiFi registration process for some carriers.
1188      *
1189      * @param isCapable true if the IMS stack should try to register for IMS over IWLAN, false
1190      *     otherwise.
1191      * @param mode the Voice over WiFi mode preference to set, which can be one of the following:
1192      * - {@link #WIFI_MODE_WIFI_ONLY}
1193      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
1194      * - {@link #WIFI_MODE_WIFI_PREFERRED}
1195      * @throws IllegalArgumentException if the subscription associated with this operation is not
1196      * active (SIM is not inserted, ESIM inactive) or invalid.
1197      * @see #setVoWiFiSettingEnabled(boolean)
1198      * @hide
1199      */
1200     @SystemApi
1201     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setVoWiFiNonPersistent(boolean isCapable, int mode)1202     public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
1203         ITelephony iTelephony = getITelephony();
1204         if (iTelephony == null) {
1205             throw new RuntimeException("Could not find Telephony Service.");
1206         }
1207 
1208         try {
1209             iTelephony.setVoWiFiNonPersistent(mSubId, isCapable, mode);
1210         } catch (ServiceSpecificException e) {
1211             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
1212                 // Rethrow as runtime error to keep API compatible.
1213                 throw new IllegalArgumentException(e.getMessage());
1214             } else {
1215                 throw new RuntimeException(e.getMessage());
1216             }
1217         } catch (RemoteException e) {
1218             throw e.rethrowAsRuntimeException();
1219         }
1220     }
1221 
1222     /**
1223      * Returns the user's voice over WiFi Roaming mode setting associated with the device.
1224      *
1225      * <p>This API requires one of the following:
1226      * <ul>
1227      *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
1228      *     <li>If the caller is the device or profile owner, the caller holds the
1229      *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
1230      *     <li>The caller has carrier privileges (see
1231      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
1232      *     active subscription.</li>
1233      * </ul>
1234      * <p>The profile owner is an app that owns a managed profile on the device; for more details
1235      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
1236      * Access by profile owners is deprecated and will be removed in a future release.
1237      *
1238      * @throws IllegalArgumentException if the subscription associated with this operation is not
1239      * active (SIM is not inserted, ESIM inactive) or invalid.
1240      * @return The Voice over WiFi Mode preference set by the user, which can be one of the
1241      * following:
1242      * - {@link #WIFI_MODE_WIFI_ONLY}
1243      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
1244      * - {@link #WIFI_MODE_WIFI_PREFERRED}
1245      */
1246     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
1247     @RequiresPermission(anyOf = {
1248             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
1249             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
getVoWiFiModeSetting()1250     public @WiFiCallingMode int getVoWiFiModeSetting() {
1251         ITelephony iTelephony = getITelephony();
1252         if (iTelephony == null) {
1253             throw new RuntimeException("Could not find Telephony Service.");
1254         }
1255 
1256         try {
1257             return iTelephony.getVoWiFiModeSetting(mSubId);
1258         } catch (ServiceSpecificException e) {
1259             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
1260                 // Rethrow as runtime error to keep API compatible.
1261                 throw new IllegalArgumentException(e.getMessage());
1262             } else {
1263                 throw new RuntimeException(e.getMessage());
1264             }
1265         } catch (RemoteException e) {
1266             throw e.rethrowAsRuntimeException();
1267         }
1268     }
1269 
1270     /**
1271      * Set the user's preference for Voice over WiFi calling mode.
1272      * @param mode The user's preference for the technology to register for IMS over, can be one of
1273      *    the following:
1274      * - {@link #WIFI_MODE_WIFI_ONLY}
1275      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
1276      * - {@link #WIFI_MODE_WIFI_PREFERRED}
1277      * @throws IllegalArgumentException if the subscription associated with this operation is not
1278      * active (SIM is not inserted, ESIM inactive) or invalid.
1279      * @see #getVoWiFiModeSetting()
1280      * @hide
1281      */
1282     @SystemApi
1283     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setVoWiFiModeSetting(@iFiCallingMode int mode)1284     public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
1285         ITelephony iTelephony = getITelephony();
1286         if (iTelephony == null) {
1287             throw new RuntimeException("Could not find Telephony Service.");
1288         }
1289 
1290         try {
1291             iTelephony.setVoWiFiModeSetting(mSubId, mode);
1292         } catch (ServiceSpecificException e) {
1293             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
1294                 // Rethrow as runtime error to keep API compatible.
1295                 throw new IllegalArgumentException(e.getMessage());
1296             } else {
1297                 throw new RuntimeException(e.getMessage());
1298             }
1299         } catch (RemoteException e) {
1300             throw e.rethrowAsRuntimeException();
1301         }
1302     }
1303 
1304     /**
1305      * Set the user's preference for Voice over WiFi calling mode while the device is roaming on
1306      * another network.
1307      *
1308      * @return The user's preference for the technology to register for IMS over when roaming on
1309      *     another network, can be one of the following:
1310      *     - {@link #WIFI_MODE_WIFI_ONLY}
1311      *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
1312      *     - {@link #WIFI_MODE_WIFI_PREFERRED}
1313      * @throws IllegalArgumentException if the subscription associated with this operation is not
1314      * active (SIM is not inserted, ESIM inactive) or invalid.
1315      * @see #setVoWiFiRoamingSettingEnabled(boolean)
1316      * @hide
1317      */
1318     @SystemApi
1319     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
getVoWiFiRoamingModeSetting()1320     public @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
1321         ITelephony iTelephony = getITelephony();
1322         if (iTelephony == null) {
1323             throw new RuntimeException("Could not find Telephony Service.");
1324         }
1325 
1326         try {
1327             return iTelephony.getVoWiFiRoamingModeSetting(mSubId);
1328         } catch (ServiceSpecificException e) {
1329             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
1330                 // Rethrow as runtime error to keep API compatible.
1331                 throw new IllegalArgumentException(e.getMessage());
1332             } else {
1333                 throw new RuntimeException(e.getMessage());
1334             }
1335         } catch (RemoteException e) {
1336             throw e.rethrowAsRuntimeException();
1337         }
1338     }
1339 
1340     /**
1341      * Set the user's preference for Voice over WiFi mode while the device is roaming on another
1342      * network.
1343      *
1344      * @param mode The user's preference for the technology to register for IMS over when roaming on
1345      *     another network, can be one of the following:
1346      *     - {@link #WIFI_MODE_WIFI_ONLY}
1347      *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
1348      *     - {@link #WIFI_MODE_WIFI_PREFERRED}
1349      * @throws IllegalArgumentException if the subscription associated with this operation is not
1350      * active (SIM is not inserted, ESIM inactive) or invalid.
1351      * @see #getVoWiFiRoamingModeSetting()
1352      * @hide
1353      */
1354     @SystemApi
1355     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setVoWiFiRoamingModeSetting(@iFiCallingMode int mode)1356     public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
1357         ITelephony iTelephony = getITelephony();
1358         if (iTelephony == null) {
1359             throw new RuntimeException("Could not find Telephony Service.");
1360         }
1361 
1362         try {
1363             iTelephony.setVoWiFiRoamingModeSetting(mSubId, mode);
1364         } catch (ServiceSpecificException e) {
1365             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
1366                 // Rethrow as runtime error to keep API compatible.
1367                 throw new IllegalArgumentException(e.getMessage());
1368             } else {
1369                 throw new RuntimeException(e.getMessage());
1370             }
1371         } catch (RemoteException e) {
1372             throw e.rethrowAsRuntimeException();
1373         }
1374     }
1375 
1376     /**
1377      * Sets the capability of RTT for IMS calls placed on this subscription.
1378      *
1379      * Note: This does not affect the value of
1380      * {@link android.provider.Settings.Secure#RTT_CALLING_MODE}, which is the global user setting
1381      * for RTT. That value is enabled/disabled separately by the user through the Accessibility
1382      * settings.
1383      * @throws IllegalArgumentException if the subscription associated with this operation is not
1384      * active (SIM is not inserted, ESIM inactive) or invalid.
1385      * @param isEnabled if true RTT should be enabled during calls made on this subscription.
1386      * @hide
1387      */
1388     @SystemApi
1389     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setRttCapabilitySetting(boolean isEnabled)1390     public void setRttCapabilitySetting(boolean isEnabled) {
1391         ITelephony iTelephony = getITelephony();
1392         if (iTelephony == null) {
1393             throw new RuntimeException("Could not find Telephony Service.");
1394         }
1395 
1396         try {
1397             iTelephony.setRttCapabilitySetting(mSubId, isEnabled);
1398         } catch (ServiceSpecificException e) {
1399             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
1400                 // Rethrow as runtime error to keep API compatible.
1401                 throw new IllegalArgumentException(e.getMessage());
1402             } else {
1403                 throw new RuntimeException(e.getMessage());
1404             }
1405         } catch (RemoteException e) {
1406             throw e.rethrowAsRuntimeException();
1407         }
1408     }
1409 
1410     /**
1411      * @return true if TTY over VoLTE is supported
1412      *
1413      * <p>This API requires one of the following:
1414      * <ul>
1415      *     <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li>
1416      *     <li>If the caller is the device or profile owner, the caller holds the
1417      *     {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li>
1418      *     <li>The caller has carrier privileges (see
1419      *     {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any
1420      *     active subscription.</li>
1421      * </ul>
1422      * <p>The profile owner is an app that owns a managed profile on the device; for more details
1423      * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
1424      * Access by profile owners is deprecated and will be removed in a future release.
1425      *
1426      * @throws IllegalArgumentException if the subscription associated with this operation is not
1427      * active (SIM is not inserted, ESIM inactive) or invalid.
1428      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
1429      */
1430     @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
1431     @RequiresPermission(anyOf = {
1432             android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
1433             android.Manifest.permission.READ_PRECISE_PHONE_STATE})
isTtyOverVolteEnabled()1434     public boolean isTtyOverVolteEnabled() {
1435         ITelephony iTelephony = getITelephony();
1436         if (iTelephony == null) {
1437             throw new RuntimeException("Could not find Telephony Service.");
1438         }
1439 
1440         try {
1441             return iTelephony.isTtyOverVolteEnabled(mSubId);
1442         } catch (ServiceSpecificException e) {
1443             if (e.errorCode == ImsException.CODE_ERROR_INVALID_SUBSCRIPTION) {
1444                 // Rethrow as runtime error to keep API compatible.
1445                 throw new IllegalArgumentException(e.getMessage());
1446             } else {
1447                 throw new RuntimeException(e.getMessage());
1448             }
1449         } catch (RemoteException e) {
1450             throw e.rethrowAsRuntimeException();
1451         }
1452     }
1453 
1454     /**
1455      * Get the status of the MmTel Feature registered on this subscription.
1456      * @param executor The executor that will be used to call the callback.
1457      * @param callback A callback containing an Integer describing the current state of the
1458      *                 MmTel feature, Which will be one of the following:
1459      *                 {@link ImsFeature#STATE_UNAVAILABLE},
1460      *                {@link ImsFeature#STATE_INITIALIZING},
1461      *                {@link ImsFeature#STATE_READY}. Will be called using the executor
1462      *                 specified when the service state has been retrieved from the IMS service.
1463      * @throws ImsException if the IMS service associated with this subscription is not available or
1464      * the IMS service is not available.
1465      * @hide
1466      */
1467     @SystemApi
1468     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
getFeatureState(@onNull @allbackExecutor Executor executor, @NonNull @ImsFeature.ImsState Consumer<Integer> callback)1469     public void getFeatureState(@NonNull @CallbackExecutor Executor executor,
1470             @NonNull @ImsFeature.ImsState Consumer<Integer> callback) throws ImsException {
1471         if (executor == null) {
1472             throw new IllegalArgumentException("Must include a non-null Executor.");
1473         }
1474         if (callback == null) {
1475             throw new IllegalArgumentException("Must include a non-null Consumer.");
1476         }
1477 
1478         ITelephony iTelephony = getITelephony();
1479         if (iTelephony == null) {
1480             throw new ImsException("Could not find Telephony Service.",
1481                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
1482         }
1483 
1484         try {
1485             iTelephony.getImsMmTelFeatureState(mSubId, new IIntegerConsumer.Stub() {
1486                 @Override
1487                 public void accept(int result) {
1488                     final long identity = Binder.clearCallingIdentity();
1489                     try {
1490                         executor.execute(() -> callback.accept(result));
1491                     } finally {
1492                         Binder.restoreCallingIdentity(identity);
1493                     }
1494                 }
1495             });
1496         } catch (ServiceSpecificException sse) {
1497             throw new ImsException(sse.getMessage(), sse.errorCode);
1498         } catch (RemoteException e) {
1499             e.rethrowAsRuntimeException();
1500         }
1501     }
1502 
1503     /**
1504      * Register a new callback, which is used to notify the registrant of changes to
1505      * the state of the underlying IMS service that is attached to telephony to
1506      * implement IMS functionality. If the manager is created for
1507      * the {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID},
1508      * this throws an {@link ImsException}.
1509      *
1510      * <p>Requires Permission:
1511      * {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE READ_PRECISE_PHONE_STATE}
1512      * or that the calling app has carrier privileges
1513      * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}).
1514      *
1515      * @param executor the Executor that will be used to call the {@link ImsStateCallback}.
1516      * @param callback The callback instance being registered.
1517      * @throws ImsException in the case that the callback can not be registered.
1518      * See {@link ImsException#getCode} for more information on when this is called.
1519      */
1520     @RequiresPermission(anyOf = {Manifest.permission.READ_PRECISE_PHONE_STATE,
1521             Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
registerImsStateCallback(@onNull Executor executor, @NonNull ImsStateCallback callback)1522     public void registerImsStateCallback(@NonNull Executor executor,
1523             @NonNull ImsStateCallback callback) throws ImsException {
1524         Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
1525         Objects.requireNonNull(executor, "Must include a non-null Executor.");
1526 
1527         callback.init(executor);
1528         ITelephony telephony = mBinderCache.listenOnBinder(callback, callback::binderDied);
1529         if (telephony == null) {
1530             throw new ImsException("Telephony server is down",
1531                     ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
1532         }
1533 
1534         try {
1535             telephony.registerImsStateCallback(
1536                     mSubId, ImsFeature.FEATURE_MMTEL,
1537                     callback.getCallbackBinder(), getOpPackageName());
1538         } catch (ServiceSpecificException e) {
1539             throw new ImsException(e.getMessage(), e.errorCode);
1540         } catch (RemoteException | IllegalStateException e) {
1541             throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
1542         }
1543     }
1544 
1545     /**
1546      * Unregisters a previously registered callback.
1547      *
1548      * @param callback The callback instance to be unregistered.
1549      */
unregisterImsStateCallback(@onNull ImsStateCallback callback)1550     public void unregisterImsStateCallback(@NonNull ImsStateCallback callback) {
1551         Objects.requireNonNull(callback, "Must include a non-null ImsStateCallback.");
1552 
1553         ITelephony telephony = mBinderCache.removeRunnable(callback);
1554         try {
1555             if (telephony != null) {
1556                 telephony.unregisterImsStateCallback(callback.getCallbackBinder());
1557             }
1558         } catch (RemoteException ignore) {
1559             // ignore it
1560         }
1561     }
1562 
getOpPackageName()1563     private String getOpPackageName() {
1564         if (mContext != null) {
1565             return mContext.getOpPackageName();
1566         } else {
1567             return null;
1568         }
1569     }
1570 
getITelephony()1571     private ITelephony getITelephony() {
1572         return mBinderCache.getBinder();
1573     }
1574 
getITelephonyInterface()1575     private static ITelephony getITelephonyInterface() {
1576         ITelephony binder = ITelephony.Stub.asInterface(
1577                 TelephonyFrameworkInitializer
1578                         .getTelephonyServiceManager()
1579                         .getTelephonyServiceRegisterer()
1580                         .get());
1581         return binder;
1582     }
1583 
1584     /**
1585      * Convert Wi-Fi calling mode to string.
1586      *
1587      * @param mode Wi-Fi calling mode.
1588      * @return The Wi-Fi calling mode in string format.
1589      *
1590      * @hide
1591      */
1592     @NonNull
wifiCallingModeToString(@msMmTelManager.WiFiCallingMode int mode)1593     public static String wifiCallingModeToString(@ImsMmTelManager.WiFiCallingMode int mode) {
1594         switch (mode) {
1595             case ImsMmTelManager.WIFI_MODE_UNKNOWN: return "UNKNOWN";
1596             case ImsMmTelManager.WIFI_MODE_WIFI_ONLY: return "WIFI_ONLY";
1597             case ImsMmTelManager.WIFI_MODE_CELLULAR_PREFERRED: return "CELLULAR_PREFERRED";
1598             case ImsMmTelManager.WIFI_MODE_WIFI_PREFERRED: return "WIFI_PREFERRED";
1599             default:
1600                 return "UNKNOWN(" + mode + ")";
1601         }
1602     }
1603 }
1604