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.Nullable;
25 import android.annotation.RequiresPermission;
26 import android.annotation.SystemApi;
27 import android.content.Context;
28 import android.content.pm.IPackageManager;
29 import android.content.pm.PackageManager;
30 import android.net.Uri;
31 import android.os.Binder;
32 import android.os.RemoteException;
33 import android.os.ServiceManager;
34 import android.telephony.AccessNetworkConstants;
35 import android.telephony.SubscriptionManager;
36 import android.telephony.ims.aidl.IImsCapabilityCallback;
37 import android.telephony.ims.aidl.IImsRegistrationCallback;
38 import android.telephony.ims.feature.ImsFeature;
39 import android.telephony.ims.feature.MmTelFeature;
40 import android.telephony.ims.stub.ImsRegistrationImplBase;
41 import android.util.Log;
42 
43 import com.android.internal.annotations.VisibleForTesting;
44 import com.android.internal.telephony.ITelephony;
45 
46 import java.lang.annotation.Retention;
47 import java.lang.annotation.RetentionPolicy;
48 import java.util.HashMap;
49 import java.util.Map;
50 import java.util.concurrent.Executor;
51 
52 /**
53  * A manager for the MmTel (Multimedia Telephony) feature of an IMS network, given an associated
54  * subscription.
55  *
56  * Allows a user to query the IMS MmTel feature information for a subscription, register for
57  * registration and MmTel capability status callbacks, as well as query/modify user settings for the
58  * associated subscription.
59  *
60  * @see #createForSubscriptionId(int)
61  * @hide
62  */
63 @SystemApi
64 public class ImsMmTelManager {
65 
66     private static final String TAG = "ImsMmTelManager";
67 
68     /**
69      * @hide
70      */
71     @Retention(RetentionPolicy.SOURCE)
72     @IntDef(prefix = "WIFI_MODE_", value = {
73             WIFI_MODE_WIFI_ONLY,
74             WIFI_MODE_CELLULAR_PREFERRED,
75             WIFI_MODE_WIFI_PREFERRED
76             })
77     public @interface WiFiCallingMode {}
78 
79     /**
80      * Register for IMS over IWLAN if WiFi signal quality is high enough. Do not hand over to LTE
81      * registration if signal quality degrades.
82      */
83     public static final int WIFI_MODE_WIFI_ONLY = 0;
84 
85     /**
86      * Prefer registering for IMS over LTE if LTE signal quality is high enough.
87      */
88     public static final int WIFI_MODE_CELLULAR_PREFERRED = 1;
89 
90     /**
91      * Prefer registering for IMS over IWLAN if possible if WiFi signal quality is high enough.
92      */
93     public static final int WIFI_MODE_WIFI_PREFERRED = 2;
94 
95     /**
96      * Callback class for receiving IMS network Registration callback events.
97      * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback)
98      * @see #unregisterImsRegistrationCallback(RegistrationCallback)
99      */
100     public static class RegistrationCallback {
101 
102         private static class RegistrationBinder extends IImsRegistrationCallback.Stub {
103 
104             // Translate ImsRegistrationImplBase API to new AccessNetworkConstant because WLAN
105             // and WWAN are more accurate constants.
106             private static final Map<Integer, Integer> IMS_REG_TO_ACCESS_TYPE_MAP =
107                     new HashMap<Integer, Integer>() {{
108                         // Map NONE to -1 to make sure that we handle the REGISTRATION_TECH_NONE
109                         // case, since it is defined.
110                         put(ImsRegistrationImplBase.REGISTRATION_TECH_NONE, -1);
111                         put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
112                                 AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
113                         put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
114                                 AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
115                     }};
116 
117             private final RegistrationCallback mLocalCallback;
118             private Executor mExecutor;
119 
RegistrationBinder(RegistrationCallback localCallback)120             RegistrationBinder(RegistrationCallback localCallback) {
121                 mLocalCallback = localCallback;
122             }
123 
124             @Override
onRegistered(int imsRadioTech)125             public void onRegistered(int imsRadioTech) {
126                 if (mLocalCallback == null) return;
127 
128                 Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
129                         mLocalCallback.onRegistered(getAccessType(imsRadioTech))));
130             }
131 
132             @Override
onRegistering(int imsRadioTech)133             public void onRegistering(int imsRadioTech) {
134                 if (mLocalCallback == null) return;
135 
136                 Binder.withCleanCallingIdentity(() -> mExecutor.execute(() ->
137                         mLocalCallback.onRegistering(getAccessType(imsRadioTech))));
138             }
139 
140             @Override
onDeregistered(ImsReasonInfo info)141             public void onDeregistered(ImsReasonInfo info) {
142                 if (mLocalCallback == null) return;
143 
144                 Binder.withCleanCallingIdentity(() ->
145                         mExecutor.execute(() -> mLocalCallback.onUnregistered(info)));
146             }
147 
148             @Override
onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info)149             public void onTechnologyChangeFailed(int imsRadioTech, ImsReasonInfo info) {
150                 if (mLocalCallback == null) return;
151 
152                 Binder.withCleanCallingIdentity(() ->
153                         mExecutor.execute(() -> mLocalCallback.onTechnologyChangeFailed(
154                                 getAccessType(imsRadioTech), info)));
155             }
156 
157             @Override
onSubscriberAssociatedUriChanged(Uri[] uris)158             public void onSubscriberAssociatedUriChanged(Uri[] uris) {
159                 if (mLocalCallback == null) return;
160 
161                 Binder.withCleanCallingIdentity(() ->
162                         mExecutor.execute(() ->
163                                 mLocalCallback.onSubscriberAssociatedUriChanged(uris)));
164             }
165 
setExecutor(Executor executor)166             private void setExecutor(Executor executor) {
167                 mExecutor = executor;
168             }
169 
getAccessType(int regType)170             private static int getAccessType(int regType) {
171                 if (!IMS_REG_TO_ACCESS_TYPE_MAP.containsKey(regType)) {
172                     Log.w("ImsMmTelManager", "RegistrationBinder - invalid regType returned: "
173                             + regType);
174                     return -1;
175                 }
176                 return IMS_REG_TO_ACCESS_TYPE_MAP.get(regType);
177             }
178         }
179 
180         private final RegistrationBinder mBinder = new RegistrationBinder(this);
181 
182         /**
183          * Notifies the framework when the IMS Provider is registered to the IMS network.
184          *
185          * @param imsTransportType the radio access technology. Valid values are defined in
186          * {@link android.telephony.AccessNetworkConstants.TransportType}.
187          */
onRegistered(int imsTransportType)188         public void onRegistered(int imsTransportType) {
189         }
190 
191         /**
192          * Notifies the framework when the IMS Provider is trying to register the IMS network.
193          *
194          * @param imsTransportType the radio access technology. Valid values are defined in
195          * {@link android.telephony.AccessNetworkConstants.TransportType}.
196          */
onRegistering(int imsTransportType)197         public void onRegistering(int imsTransportType) {
198         }
199 
200         /**
201          * Notifies the framework when the IMS Provider is deregistered from the IMS network.
202          *
203          * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
204          */
onUnregistered(@ullable ImsReasonInfo info)205         public void onUnregistered(@Nullable ImsReasonInfo info) {
206         }
207 
208         /**
209          * A failure has occurred when trying to handover registration to another technology type,
210          * defined in {@link android.telephony.AccessNetworkConstants.TransportType}
211          *
212          * @param imsTransportType The
213          *         {@link android.telephony.AccessNetworkConstants.TransportType}
214          *         transport type that has failed to handover registration to.
215          * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
216          */
onTechnologyChangeFailed(int imsTransportType, @Nullable ImsReasonInfo info)217         public void onTechnologyChangeFailed(int imsTransportType, @Nullable ImsReasonInfo info) {
218         }
219 
220         /**
221          * Returns a list of subscriber {@link Uri}s associated with this IMS subscription when
222          * it changes. Per RFC3455, an associated URI is a URI that the service provider has
223          * allocated to a user for their own usage. A user's phone number is typically one of the
224          * associated URIs.
225          * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
226          *         subscription.
227          * @hide
228          */
onSubscriberAssociatedUriChanged(@ullable Uri[] uris)229         public void onSubscriberAssociatedUriChanged(@Nullable Uri[] uris) {
230         }
231 
232         /**@hide*/
getBinder()233         public final IImsRegistrationCallback getBinder() {
234             return mBinder;
235         }
236 
237         /**@hide*/
238         //Only exposed as public for compatibility with deprecated ImsManager APIs.
setExecutor(Executor executor)239         public void setExecutor(Executor executor) {
240             mBinder.setExecutor(executor);
241         }
242     }
243 
244     /**
245      * Receives IMS capability status updates from the ImsService. This information is also
246      * available via the {@link #isAvailable(int, int)} method below.
247      *
248      * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback)
249      * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
250      */
251     public static class CapabilityCallback {
252 
253         private static class CapabilityBinder extends IImsCapabilityCallback.Stub {
254 
255             private final CapabilityCallback mLocalCallback;
256             private Executor mExecutor;
257 
CapabilityBinder(CapabilityCallback c)258             CapabilityBinder(CapabilityCallback c) {
259                 mLocalCallback = c;
260             }
261 
262             @Override
onCapabilitiesStatusChanged(int config)263             public void onCapabilitiesStatusChanged(int config) {
264                 if (mLocalCallback == null) return;
265 
266                 Binder.withCleanCallingIdentity(() ->
267                         mExecutor.execute(() -> mLocalCallback.onCapabilitiesStatusChanged(
268                                 new MmTelFeature.MmTelCapabilities(config))));
269             }
270 
271             @Override
onQueryCapabilityConfiguration(int capability, int radioTech, boolean isEnabled)272             public void onQueryCapabilityConfiguration(int capability, int radioTech,
273                     boolean isEnabled) {
274                 // This is not used for public interfaces.
275             }
276 
277             @Override
onChangeCapabilityConfigurationError(int capability, int radioTech, @ImsFeature.ImsCapabilityError int reason)278             public void onChangeCapabilityConfigurationError(int capability, int radioTech,
279                     @ImsFeature.ImsCapabilityError int reason) {
280                 // This is not used for public interfaces
281             }
282 
setExecutor(Executor executor)283             private void setExecutor(Executor executor) {
284                 mExecutor = executor;
285             }
286         }
287 
288         private final CapabilityBinder mBinder = new CapabilityBinder(this);
289 
290         /**
291          * The status of the feature's capabilities has changed to either available or unavailable.
292          * If unavailable, the feature is not able to support the unavailable capability at this
293          * time.
294          *
295          * This information can also be queried using the {@link #isAvailable(int, int)} API.
296          *
297          * @param capabilities The new availability of the capabilities.
298          */
onCapabilitiesStatusChanged( @onNull MmTelFeature.MmTelCapabilities capabilities)299         public void onCapabilitiesStatusChanged(
300                 @NonNull MmTelFeature.MmTelCapabilities capabilities) {
301         }
302 
303         /**@hide*/
getBinder()304         public final IImsCapabilityCallback getBinder() {
305             return mBinder;
306         }
307 
308         /**@hide*/
309         // Only exposed as public method for compatibility with deprecated ImsManager APIs.
310         // TODO: clean up dependencies and change back to private visibility.
setExecutor(Executor executor)311         public final void setExecutor(Executor executor) {
312             mBinder.setExecutor(executor);
313         }
314     }
315 
316     private int mSubId;
317 
318     /**
319      * Create an instance of {@link ImsMmTelManager} for the subscription id specified.
320      *
321      * @param subId The ID of the subscription that this ImsMmTelManager will use.
322      * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
323      * @throws IllegalArgumentException if the subscription is invalid.
324      */
createForSubscriptionId(int subId)325     public static @NonNull ImsMmTelManager createForSubscriptionId(int subId) {
326         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
327             throw new IllegalArgumentException("Invalid subscription ID");
328         }
329 
330         return new ImsMmTelManager(subId);
331     }
332 
333     /**
334      * Only visible for testing, use {@link #createForSubscriptionId(int)} instead.
335      * @hide
336      */
337     @VisibleForTesting
ImsMmTelManager(int subId)338     public ImsMmTelManager(int subId) {
339         mSubId = subId;
340     }
341 
342     /**
343      * Registers a {@link RegistrationCallback} with the system, which will provide registration
344      * updates for the subscription specified in {@link #createForSubscriptionId(int)}. Use
345      * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
346      * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
347      *
348      * When the callback is registered, it will initiate the callback c to be called with the
349      * current registration state.
350      *
351      * @param executor The executor the callback events should be run on.
352      * @param c The {@link RegistrationCallback} to be added.
353      * @see #unregisterImsRegistrationCallback(RegistrationCallback)
354      * @throws IllegalArgumentException if the subscription associated with this callback is not
355      * active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or
356      * {@link CapabilityCallback} callback.
357      * @throws ImsException if the subscription associated with this callback is valid, but
358      * the {@link ImsService} associated with the subscription is not available. This can happen if
359      * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
360      * reason.
361      */
362     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
registerImsRegistrationCallback(@onNull @allbackExecutor Executor executor, @NonNull RegistrationCallback c)363     public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor,
364             @NonNull RegistrationCallback c) throws ImsException {
365         if (c == null) {
366             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
367         }
368         if (executor == null) {
369             throw new IllegalArgumentException("Must include a non-null Executor.");
370         }
371         if (!isImsAvailableOnDevice()) {
372             throw new ImsException("IMS not available on device.",
373                     ImsException.CODE_ERROR_UNSUPPORTED_OPERATION);
374         }
375         c.setExecutor(executor);
376         try {
377             getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
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      * @throws IllegalArgumentException if the subscription ID associated with this callback is
394      * invalid.
395      */
396     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
unregisterImsRegistrationCallback(@onNull RegistrationCallback c)397     public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) {
398         if (c == null) {
399             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
400         }
401         try {
402             getITelephony().unregisterImsRegistrationCallback(mSubId, c.getBinder());
403         } catch (RemoteException e) {
404             throw e.rethrowAsRuntimeException();
405         }
406     }
407 
408     /**
409      * Registers a {@link CapabilityCallback} with the system, which will provide MmTel service
410      * availability updates for the subscription specified in
411      * {@link #createForSubscriptionId(int)}. The method {@link #isAvailable(int, int)}
412      * can also be used to query this information at any time.
413      *
414      * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
415      * subscription changed events and call
416      * {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
417      *
418      * When the callback is registered, it will initiate the callback c to be called with the
419      * current capabilities.
420      *
421      * @param executor The executor the callback events should be run on.
422      * @param c The MmTel {@link CapabilityCallback} to be registered.
423      * @see #unregisterMmTelCapabilityCallback(CapabilityCallback)
424      * @throws IllegalArgumentException if the subscription associated with this callback is not
425      * active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or
426      * {@link CapabilityCallback} callback.
427      * @throws ImsException if the subscription associated with this callback is valid, but
428      * the {@link ImsService} associated with the subscription is not available. This can happen if
429      * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
430      * reason.
431      */
432     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
registerMmTelCapabilityCallback(@onNull @allbackExecutor Executor executor, @NonNull CapabilityCallback c)433     public void registerMmTelCapabilityCallback(@NonNull @CallbackExecutor Executor executor,
434             @NonNull CapabilityCallback c) throws ImsException {
435         if (c == null) {
436             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
437         }
438         if (executor == null) {
439             throw new IllegalArgumentException("Must include a non-null Executor.");
440         }
441         if (!isImsAvailableOnDevice()) {
442             throw new ImsException("IMS not available on device.",
443                     ImsException.CODE_ERROR_UNSUPPORTED_OPERATION);
444         }
445         c.setExecutor(executor);
446         try {
447             getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder());
448         } catch (RemoteException e) {
449             throw e.rethrowAsRuntimeException();
450         }  catch (IllegalStateException e) {
451             throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
452         }
453     }
454 
455     /**
456      * Removes an existing MmTel {@link CapabilityCallback}.
457      *
458      * When the subscription associated with this callback is removed (SIM removed, ESIM swap,
459      * etc...), this callback will automatically be removed. If this method is called for an
460      * inactive subscription, it will result in a no-op.
461      * @param c The MmTel {@link CapabilityCallback} to be removed.
462      * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback)
463      * @throws IllegalArgumentException if the subscription ID associated with this callback is
464      * invalid.
465      */
466     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
unregisterMmTelCapabilityCallback(@onNull CapabilityCallback c)467     public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) {
468         if (c == null) {
469             throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
470         }
471         try {
472             getITelephony().unregisterMmTelCapabilityCallback(mSubId, c.getBinder());
473         } catch (RemoteException e) {
474             throw e.rethrowAsRuntimeException();
475         }
476     }
477 
478     /**
479      * Query the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
480      * enable MmTel IMS features, depending on the carrier configuration for the current
481      * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
482      * be enabled as long as the carrier has provisioned these services for the specified
483      * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
484      * carrier requirements.
485      *
486      * Modifying this value may also trigger an IMS registration or deregistration, depending on
487      * whether or not the new value is enabled or disabled.
488      *
489      * Note: If the carrier configuration for advanced calling is not editable or hidden, this
490      * method will do nothing and will instead always use the default value.
491      *
492      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
493      * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
494      * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
495      * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
496      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
497      * @see #setAdvancedCallingSettingEnabled(boolean)
498      * @return true if the user's setting for advanced calling is enabled, false otherwise.
499      */
500     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
isAdvancedCallingSettingEnabled()501     public boolean isAdvancedCallingSettingEnabled() {
502         try {
503             return getITelephony().isAdvancedCallingSettingEnabled(mSubId);
504         } catch (RemoteException e) {
505             throw e.rethrowAsRuntimeException();
506         }
507     }
508 
509     /**
510      * Modify the user’s setting for “Advanced Calling” or "Enhanced 4G LTE", which is used to
511      * enable MmTel IMS features, depending on the carrier configuration for the current
512      * subscription. If this setting is enabled, IMS voice and video telephony over IWLAN/LTE will
513      * be enabled as long as the carrier has provisioned these services for the specified
514      * subscription. Other IMS services (SMS/UT) are not affected by this user setting and depend on
515      * carrier requirements.
516      *
517      * Modifying this value may also trigger an IMS registration or deregistration, depending on
518      * whether or not the new value is enabled or disabled.
519      *
520      * Note: If the carrier configuration for advanced calling is not editable or hidden, this
521      * method will do nothing and will instead always use the default value.
522      *
523      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL
524      * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL
525      * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL
526      * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL
527      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
528      * @see #isAdvancedCallingSettingEnabled()
529      */
530     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setAdvancedCallingSettingEnabled(boolean isEnabled)531     public void setAdvancedCallingSettingEnabled(boolean isEnabled) {
532         try {
533             getITelephony().setAdvancedCallingSettingEnabled(mSubId, isEnabled);
534             return;
535         } catch (RemoteException e) {
536             throw e.rethrowAsRuntimeException();
537         }
538     }
539 
540     /**
541      * Query the IMS MmTel capability for a given registration technology. This does not
542      * necessarily mean that we are registered and the capability is available, but rather the
543      * subscription is capable of this service over IMS.
544      *
545      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL
546      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VT_AVAILABLE_BOOL
547      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_IMS_GBA_REQUIRED_BOOL
548      * @see #isAvailable(int, int)
549      *
550      * @param imsRegTech The IMS registration technology, can be one of the following:
551      *         {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
552      *         {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
553      * @param capability The IMS MmTel capability to query, can be one of the following:
554      *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
555      *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO,
556      *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT},
557      *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
558      * @return {@code true} if the MmTel IMS capability is capable for this subscription, false
559      *         otherwise.
560      */
561     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
isCapable(@mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech)562     public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
563             @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
564         try {
565             return getITelephony().isCapable(mSubId, capability, imsRegTech);
566         } catch (RemoteException e) {
567             throw e.rethrowAsRuntimeException();
568         }
569     }
570 
571     /**
572      * Query the availability of an IMS MmTel capability for a given registration technology. If
573      * a capability is available, IMS is registered and the service is currently available over IMS.
574      *
575      * @see #isCapable(int, int)
576      *
577      * @param imsRegTech The IMS registration technology, can be one of the following:
578      *         {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE},
579      *         {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN}
580      * @param capability The IMS MmTel capability to query, can be one of the following:
581      *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE},
582      *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO,
583      *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT},
584      *         {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS}
585      * @return {@code true} if the MmTel IMS capability is available for this subscription, false
586      *         otherwise.
587      */
588     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
isAvailable(@mTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech)589     public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability,
590             @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) {
591         try {
592             return getITelephony().isAvailable(mSubId, capability, imsRegTech);
593         } catch (RemoteException e) {
594             throw e.rethrowAsRuntimeException();
595         }
596     }
597 
598     /**
599      * The user's setting for whether or not they have enabled the "Video Calling" setting.
600      * @return true if the user’s “Video Calling” setting is currently enabled.
601      * @see #setVtSettingEnabled(boolean)
602      */
603     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
isVtSettingEnabled()604     public boolean isVtSettingEnabled() {
605         try {
606             return getITelephony().isVtSettingEnabled(mSubId);
607         } catch (RemoteException e) {
608             throw e.rethrowAsRuntimeException();
609         }
610     }
611 
612     /**
613      * Change the user's setting for Video Telephony and enable the Video Telephony capability.
614      * @see #isVtSettingEnabled()
615      */
616     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setVtSettingEnabled(boolean isEnabled)617     public void setVtSettingEnabled(boolean isEnabled) {
618         try {
619             getITelephony().setVtSettingEnabled(mSubId, isEnabled);
620             return;
621         } catch (RemoteException e) {
622             throw e.rethrowAsRuntimeException();
623         }
624     }
625 
626     /**
627      * @return true if the user's setting for Voice over WiFi is enabled and false if it is not.
628      * @see #setVoWiFiSettingEnabled(boolean)
629      */
630     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
isVoWiFiSettingEnabled()631     public boolean isVoWiFiSettingEnabled() {
632         try {
633             return getITelephony().isVoWiFiSettingEnabled(mSubId);
634         } catch (RemoteException e) {
635             throw e.rethrowAsRuntimeException();
636         }
637     }
638 
639     /**
640      * Sets the user's setting for whether or not Voice over WiFi is enabled.
641      * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise=
642      * @see #isVoWiFiSettingEnabled()
643      */
644     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setVoWiFiSettingEnabled(boolean isEnabled)645     public void setVoWiFiSettingEnabled(boolean isEnabled) {
646         try {
647             getITelephony().setVoWiFiSettingEnabled(mSubId, isEnabled);
648             return;
649         } catch (RemoteException e) {
650             throw e.rethrowAsRuntimeException();
651         }
652     }
653 
654     /**
655      * @return true if the user's setting for Voice over WiFi while roaming is enabled, false
656      * if disabled.
657      * @see #setVoWiFiRoamingSettingEnabled(boolean)
658      */
659     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
isVoWiFiRoamingSettingEnabled()660     public boolean isVoWiFiRoamingSettingEnabled() {
661         try {
662             return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId);
663         } catch (RemoteException e) {
664             throw e.rethrowAsRuntimeException();
665         }
666     }
667 
668     /**
669      * Change the user's setting for Voice over WiFi while roaming.
670      * @param isEnabled true if the user's setting for Voice over WiFi while roaming is enabled,
671      *     false otherwise.
672      * @see #isVoWiFiRoamingSettingEnabled()
673      */
674     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setVoWiFiRoamingSettingEnabled(boolean isEnabled)675     public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) {
676         try {
677             getITelephony().setVoWiFiRoamingSettingEnabled(mSubId, isEnabled);
678             return;
679         } catch (RemoteException e) {
680             throw e.rethrowAsRuntimeException();
681         }
682     }
683 
684     /**
685      * Overrides the Voice over WiFi capability to true for IMS, but do not persist the setting.
686      * Typically used during the Voice over WiFi registration process for some carriers.
687      *
688      * @param isCapable true if the IMS stack should try to register for IMS over IWLAN, false
689      *     otherwise.
690      * @param mode the Voice over WiFi mode preference to set, which can be one of the following:
691      * - {@link #WIFI_MODE_WIFI_ONLY}
692      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
693      * - {@link #WIFI_MODE_WIFI_PREFERRED}
694      * @see #setVoWiFiSettingEnabled(boolean)
695      */
696     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setVoWiFiNonPersistent(boolean isCapable, int mode)697     public void setVoWiFiNonPersistent(boolean isCapable, int mode) {
698         try {
699             getITelephony().setVoWiFiNonPersistent(mSubId, isCapable, mode);
700             return;
701         } catch (RemoteException e) {
702             throw e.rethrowAsRuntimeException();
703         }
704     }
705 
706     /**
707      * @return The Voice over WiFi Mode preference set by the user, which can be one of the
708      * following:
709      * - {@link #WIFI_MODE_WIFI_ONLY}
710      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
711      * - {@link #WIFI_MODE_WIFI_PREFERRED}
712      * @see #setVoWiFiSettingEnabled(boolean)
713      */
714     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
getVoWiFiModeSetting()715     public @WiFiCallingMode int getVoWiFiModeSetting() {
716         try {
717             return getITelephony().getVoWiFiModeSetting(mSubId);
718         } catch (RemoteException e) {
719             throw e.rethrowAsRuntimeException();
720         }
721     }
722 
723     /**
724      * Set the user's preference for Voice over WiFi calling mode.
725      * @param mode The user's preference for the technology to register for IMS over, can be one of
726      *    the following:
727      * - {@link #WIFI_MODE_WIFI_ONLY}
728      * - {@link #WIFI_MODE_CELLULAR_PREFERRED}
729      * - {@link #WIFI_MODE_WIFI_PREFERRED}
730      * @see #getVoWiFiModeSetting()
731      */
732     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setVoWiFiModeSetting(@iFiCallingMode int mode)733     public void setVoWiFiModeSetting(@WiFiCallingMode int mode) {
734         try {
735             getITelephony().setVoWiFiModeSetting(mSubId, mode);
736             return;
737         } catch (RemoteException e) {
738             throw e.rethrowAsRuntimeException();
739         }
740     }
741 
742     /**
743      * Set the user's preference for Voice over WiFi calling mode while the device is roaming on
744      * another network.
745      *
746      * @return The user's preference for the technology to register for IMS over when roaming on
747      *     another network, can be one of the following:
748      *     - {@link #WIFI_MODE_WIFI_ONLY}
749      *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
750      *     - {@link #WIFI_MODE_WIFI_PREFERRED}
751      * @see #setVoWiFiRoamingSettingEnabled(boolean)
752      */
753     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
getVoWiFiRoamingModeSetting()754     public @WiFiCallingMode int getVoWiFiRoamingModeSetting() {
755         try {
756             return getITelephony().getVoWiFiRoamingModeSetting(mSubId);
757         } catch (RemoteException e) {
758             throw e.rethrowAsRuntimeException();
759         }
760     }
761 
762     /**
763      * Set the user's preference for Voice over WiFi mode while the device is roaming on another
764      * network.
765      *
766      * @param mode The user's preference for the technology to register for IMS over when roaming on
767      *     another network, can be one of the following:
768      *     - {@link #WIFI_MODE_WIFI_ONLY}
769      *     - {@link #WIFI_MODE_CELLULAR_PREFERRED}
770      *     - {@link #WIFI_MODE_WIFI_PREFERRED}
771      * @see #getVoWiFiRoamingModeSetting()
772      */
773     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setVoWiFiRoamingModeSetting(@iFiCallingMode int mode)774     public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) {
775         try {
776             getITelephony().setVoWiFiRoamingModeSetting(mSubId, mode);
777             return;
778         } catch (RemoteException e) {
779             throw e.rethrowAsRuntimeException();
780         }
781     }
782 
783     /**
784      * Sets the capability of RTT for IMS calls placed on this subscription.
785      *
786      * Note: This does not affect the value of
787      * {@link android.provider.Settings.Secure#RTT_CALLING_MODE}, which is the global user setting
788      * for RTT. That value is enabled/disabled separately by the user through the Accessibility
789      * settings.
790      * @param isEnabled if true RTT should be enabled during calls made on this subscription.
791      */
792     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
setRttCapabilitySetting(boolean isEnabled)793     public void setRttCapabilitySetting(boolean isEnabled) {
794         try {
795             getITelephony().setRttCapabilitySetting(mSubId, isEnabled);
796             return;
797         } catch (RemoteException e) {
798             throw e.rethrowAsRuntimeException();
799         }
800     }
801 
802     /**
803      * @return true if TTY over VoLTE is supported
804      * @see android.telecom.TelecomManager#getCurrentTtyMode
805      * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL
806      */
807     @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
isTtyOverVolteEnabled()808     boolean isTtyOverVolteEnabled() {
809         try {
810             return getITelephony().isTtyOverVolteEnabled(mSubId);
811         } catch (RemoteException e) {
812             throw e.rethrowAsRuntimeException();
813         }
814     }
815 
isImsAvailableOnDevice()816     private static boolean isImsAvailableOnDevice() {
817         IPackageManager pm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
818         if (pm == null) {
819             // For some reason package manger is not available.. This will fail internally anyways,
820             // so do not throw error and allow.
821             return true;
822         }
823         try {
824             return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_IMS, 0);
825         } catch (RemoteException e) {
826             // For some reason package manger is not available.. This will fail internally anyways,
827             // so do not throw error and allow.
828         }
829         return true;
830     }
831 
getITelephony()832     private static ITelephony getITelephony() {
833         ITelephony binder = ITelephony.Stub.asInterface(
834                 ServiceManager.getService(Context.TELEPHONY_SERVICE));
835         if (binder == null) {
836             throw new RuntimeException("Could not find Telephony Service.");
837         }
838         return binder;
839     }
840 }
841