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