1 /*
2  * Copyright (C) 2015 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 com.android.contacts.common.compat;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.net.Uri;
22 import android.os.Build.VERSION;
23 import android.os.Build.VERSION_CODES;
24 import android.support.annotation.Nullable;
25 import android.support.v4.os.BuildCompat;
26 import android.telecom.PhoneAccountHandle;
27 import android.telephony.TelephonyManager;
28 import com.android.dialer.common.Assert;
29 import com.android.dialer.common.LogUtil;
30 import com.android.dialer.compat.CompatUtils;
31 import java.lang.reflect.InvocationTargetException;
32 
33 public class TelephonyManagerCompat {
34 
35   // TODO: Use public API for these constants when available
36   public static final String EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE =
37       "android.telephony.event.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE";
38   public static final String EVENT_HANDOVER_TO_WIFI_FAILED =
39       "android.telephony.event.EVENT_HANDOVER_TO_WIFI_FAILED";
40   public static final String EVENT_CALL_REMOTELY_HELD = "android.telecom.event.CALL_REMOTELY_HELD";
41   public static final String EVENT_CALL_REMOTELY_UNHELD =
42       "android.telecom.event.CALL_REMOTELY_UNHELD";
43 
44   public static final String EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC =
45       "android.telephony.event.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC";
46 
47   public static final String TELEPHONY_MANAGER_CLASS = "android.telephony.TelephonyManager";
48 
49   private static final String SECRET_CODE_ACTION = "android.provider.Telephony.SECRET_CODE";
50 
51   /**
52    * @param telephonyManager The telephony manager instance to use for method calls.
53    * @return true if the current device is "voice capable".
54    *     <p>"Voice capable" means that this device supports circuit-switched (i.e. voice) phone
55    *     calls over the telephony network, and is allowed to display the in-call UI while a cellular
56    *     voice call is active. This will be false on "data only" devices which can't make voice
57    *     calls and don't support any in-call UI.
58    *     <p>Note: the meaning of this flag is subtly different from the
59    *     PackageManager.FEATURE_TELEPHONY system feature, which is available on any device with a
60    *     telephony radio, even if the device is data-only.
61    */
isVoiceCapable(@ullable TelephonyManager telephonyManager)62   public static boolean isVoiceCapable(@Nullable TelephonyManager telephonyManager) {
63     if (telephonyManager == null) {
64       return false;
65     }
66     if (CompatUtils.isLollipopMr1Compatible()
67         || CompatUtils.isMethodAvailable(TELEPHONY_MANAGER_CLASS, "isVoiceCapable")) {
68       // isVoiceCapable was unhidden in L-MR1
69       return telephonyManager.isVoiceCapable();
70     }
71     final int phoneType = telephonyManager.getPhoneType();
72     return phoneType == TelephonyManager.PHONE_TYPE_CDMA
73         || phoneType == TelephonyManager.PHONE_TYPE_GSM;
74   }
75 
76   /**
77    * Returns the number of phones available. Returns 1 for Single standby mode (Single SIM
78    * functionality) Returns 2 for Dual standby mode.(Dual SIM functionality)
79    *
80    * <p>Returns 1 if the method or telephonyManager is not available.
81    *
82    * @param telephonyManager The telephony manager instance to use for method calls.
83    */
getPhoneCount(@ullable TelephonyManager telephonyManager)84   public static int getPhoneCount(@Nullable TelephonyManager telephonyManager) {
85     if (telephonyManager == null) {
86       return 1;
87     }
88     if (CompatUtils.isMarshmallowCompatible()
89         || CompatUtils.isMethodAvailable(TELEPHONY_MANAGER_CLASS, "getPhoneCount")) {
90       return telephonyManager.getPhoneCount();
91     }
92     return 1;
93   }
94 
95   /**
96    * Returns the unique device ID of a subscription, for example, the IMEI for GSM and the MEID for
97    * CDMA phones. Return null if device ID is not available.
98    *
99    * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
100    *
101    * @param telephonyManager The telephony manager instance to use for method calls.
102    * @param slotId of which deviceID is returned
103    */
getDeviceId(@ullable TelephonyManager telephonyManager, int slotId)104   public static String getDeviceId(@Nullable TelephonyManager telephonyManager, int slotId) {
105     if (telephonyManager == null) {
106       return null;
107     }
108     if (CompatUtils.isMarshmallowCompatible()
109         || CompatUtils.isMethodAvailable(TELEPHONY_MANAGER_CLASS, "getDeviceId", Integer.class)) {
110       return telephonyManager.getDeviceId(slotId);
111     }
112     return null;
113   }
114 
115   /**
116    * Whether the phone supports TTY mode.
117    *
118    * @param telephonyManager The telephony manager instance to use for method calls.
119    * @return {@code true} if the device supports TTY mode, and {@code false} otherwise.
120    */
isTtyModeSupported(@ullable TelephonyManager telephonyManager)121   public static boolean isTtyModeSupported(@Nullable TelephonyManager telephonyManager) {
122     if (telephonyManager == null) {
123       return false;
124     }
125     if (CompatUtils.isMarshmallowCompatible()
126         || CompatUtils.isMethodAvailable(TELEPHONY_MANAGER_CLASS, "isTtyModeSupported")) {
127       return telephonyManager.isTtyModeSupported();
128     }
129     return false;
130   }
131 
132   /**
133    * Whether the phone supports hearing aid compatibility.
134    *
135    * @param telephonyManager The telephony manager instance to use for method calls.
136    * @return {@code true} if the device supports hearing aid compatibility, and {@code false}
137    *     otherwise.
138    */
isHearingAidCompatibilitySupported( @ullable TelephonyManager telephonyManager)139   public static boolean isHearingAidCompatibilitySupported(
140       @Nullable TelephonyManager telephonyManager) {
141     if (telephonyManager == null) {
142       return false;
143     }
144     if (CompatUtils.isMarshmallowCompatible()
145         || CompatUtils.isMethodAvailable(
146             TELEPHONY_MANAGER_CLASS, "isHearingAidCompatibilitySupported")) {
147       return telephonyManager.isHearingAidCompatibilitySupported();
148     }
149     return false;
150   }
151 
152   /**
153    * Returns the URI for the per-account voicemail ringtone set in Phone settings.
154    *
155    * @param telephonyManager The telephony manager instance to use for method calls.
156    * @param accountHandle The handle for the {@link android.telecom.PhoneAccount} for which to
157    *     retrieve the voicemail ringtone.
158    * @return The URI for the ringtone to play when receiving a voicemail from a specific
159    *     PhoneAccount.
160    */
161   @Nullable
getVoicemailRingtoneUri( TelephonyManager telephonyManager, PhoneAccountHandle accountHandle)162   public static Uri getVoicemailRingtoneUri(
163       TelephonyManager telephonyManager, PhoneAccountHandle accountHandle) {
164     if (VERSION.SDK_INT < VERSION_CODES.N) {
165       return null;
166     }
167     return telephonyManager.getVoicemailRingtoneUri(accountHandle);
168   }
169 
170   /**
171    * Returns whether vibration is set for voicemail notification in Phone settings.
172    *
173    * @param telephonyManager The telephony manager instance to use for method calls.
174    * @param accountHandle The handle for the {@link android.telecom.PhoneAccount} for which to
175    *     retrieve the voicemail vibration setting.
176    * @return {@code true} if the vibration is set for this PhoneAccount, {@code false} otherwise.
177    */
isVoicemailVibrationEnabled( TelephonyManager telephonyManager, PhoneAccountHandle accountHandle)178   public static boolean isVoicemailVibrationEnabled(
179       TelephonyManager telephonyManager, PhoneAccountHandle accountHandle) {
180     return VERSION.SDK_INT < VERSION_CODES.N
181         || telephonyManager.isVoicemailVibrationEnabled(accountHandle);
182   }
183 
184   /**
185    * This method uses a new system API to enable or disable visual voicemail. TODO: restrict
186    * to N MR1, not needed in future SDK.
187    */
setVisualVoicemailEnabled( TelephonyManager telephonyManager, PhoneAccountHandle handle, boolean enabled)188   public static void setVisualVoicemailEnabled(
189       TelephonyManager telephonyManager, PhoneAccountHandle handle, boolean enabled) {
190     if (VERSION.SDK_INT < VERSION_CODES.N_MR1) {
191       Assert.fail("setVisualVoicemailEnabled called on pre-NMR1");
192     }
193     try {
194       TelephonyManager.class
195           .getMethod("setVisualVoicemailEnabled", PhoneAccountHandle.class, boolean.class)
196           .invoke(telephonyManager, handle, enabled);
197     } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
198       LogUtil.e("TelephonyManagerCompat.setVisualVoicemailEnabled", "failed", e);
199     }
200   }
201 
202   /**
203    * This method uses a new system API to check if visual voicemail is enabled TODO: restrict
204    * to N MR1, not needed in future SDK.
205    */
isVisualVoicemailEnabled( TelephonyManager telephonyManager, PhoneAccountHandle handle)206   public static boolean isVisualVoicemailEnabled(
207       TelephonyManager telephonyManager, PhoneAccountHandle handle) {
208     if (VERSION.SDK_INT < VERSION_CODES.N_MR1) {
209       Assert.fail("isVisualVoicemailEnabled called on pre-NMR1");
210     }
211     try {
212       return (boolean)
213           TelephonyManager.class
214               .getMethod("isVisualVoicemailEnabled", PhoneAccountHandle.class)
215               .invoke(telephonyManager, handle);
216     } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
217       LogUtil.e("TelephonyManagerCompat.setVisualVoicemailEnabled", "failed", e);
218     }
219     return false;
220   }
221 
222   /**
223    * Handles secret codes to launch arbitrary activities.
224    *
225    * @param context the context to use
226    * @param secretCode the secret code without the "*#*#" prefix and "#*#*" suffix
227    */
handleSecretCode(Context context, String secretCode)228   public static void handleSecretCode(Context context, String secretCode) {
229     // Must use system service on O+ to avoid using broadcasts, which are not allowed on O+.
230     if (BuildCompat.isAtLeastO()) {
231       context.getSystemService(TelephonyManager.class).sendDialerSpecialCode(secretCode);
232     } else {
233       // System service call is not supported pre-O, so must use a broadcast for N-.
234       Intent intent =
235           new Intent(SECRET_CODE_ACTION, Uri.parse("android_secret_code://" + secretCode));
236       context.sendBroadcast(intent);
237     }
238   }
239 }
240