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; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.net.Uri; 22 import android.os.Bundle; 23 import android.os.PersistableBundle; 24 import android.telecom.PhoneAccount; 25 import android.telecom.PhoneAccountHandle; 26 import android.telecom.TelecomManager; 27 import android.telecom.VideoProfile; 28 import android.telephony.CarrierConfigManager; 29 import android.telephony.PhoneNumberUtils; 30 import android.text.TextUtils; 31 import android.util.Log; 32 33 import com.android.contacts.compat.CompatUtils; 34 import com.android.contacts.compat.PhoneAccountSdkCompat; 35 import com.android.contacts.util.PermissionsUtil; 36 import com.android.contacts.util.PhoneNumberHelper; 37 import com.android.contactsbind.FeedbackHelper; 38 import com.android.contactsbind.experiments.Flags; 39 import com.android.phone.common.PhoneConstants; 40 41 import java.util.List; 42 43 /** 44 * Utilities related to calls that can be used by non system apps. These 45 * use {@link Intent#ACTION_CALL} instead of ACTION_CALL_PRIVILEGED. 46 * 47 * The privileged version of this util exists inside Dialer. 48 */ 49 public class CallUtil { 50 51 public static final String TAG = "CallUtil"; 52 53 /** 54 * Indicates that the video calling is not available. 55 */ 56 public static final int VIDEO_CALLING_DISABLED = 0; 57 58 /** 59 * Indicates that video calling is enabled, regardless of presence status. 60 */ 61 public static final int VIDEO_CALLING_ENABLED = 1; 62 63 /** 64 * Indicates that video calling is enabled, but the availability of video call affordances is 65 * determined by the presence status associated with contacts. 66 */ 67 public static final int VIDEO_CALLING_PRESENCE = 2; 68 69 /** {@link PhoneAccount#EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK} */ 70 private static final String EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK = 71 "android.telecom.extra.SUPPORTS_VIDEO_CALLING_FALLBACK"; 72 73 /** {@link CarrierConfigManager#CONFIG_ALLOW_VIDEO_CALLING_FALLBACK} */ 74 private static final String CONFIG_ALLOW_VIDEO_CALLING_FALLBACK = 75 "allow_video_calling_fallback_bool"; 76 77 /** 78 * Return an Intent for making a phone call. Scheme (e.g. tel, sip) will be determined 79 * automatically. 80 */ getCallWithSubjectIntent(String number, PhoneAccountHandle phoneAccountHandle, String callSubject)81 public static Intent getCallWithSubjectIntent(String number, 82 PhoneAccountHandle phoneAccountHandle, String callSubject) { 83 84 final Intent intent = getCallIntent(getCallUri(number)); 85 intent.putExtra(TelecomManager.EXTRA_CALL_SUBJECT, callSubject); 86 if (phoneAccountHandle != null) { 87 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle); 88 } 89 return intent; 90 } 91 92 /** 93 * Return an Intent for making a phone call. Scheme (e.g. tel, sip) will be determined 94 * automatically. 95 */ getCallIntent(String number)96 public static Intent getCallIntent(String number) { 97 Uri uri = getCallUri(number); 98 return PhoneNumberUtils.isEmergencyNumber(number) 99 ? getCallIntentForEmergencyNumber(uri) : getCallIntent(uri); 100 } 101 102 /** 103 * Return an Intent to directly start Dialer when calling an emergency number. Scheme is always 104 * PhoneAccount.SCHEME_TEL. 105 */ getCallIntentForEmergencyNumber(Uri uri)106 private static Intent getCallIntentForEmergencyNumber(Uri uri) { 107 return new Intent(Intent.ACTION_DIAL, uri); 108 } 109 110 /** 111 * Return an Intent for making a phone call. A given Uri will be used as is (without any 112 * sanity check). 113 */ getCallIntent(Uri uri)114 public static Intent getCallIntent(Uri uri) { 115 return new Intent(Intent.ACTION_CALL, uri); 116 } 117 118 /** 119 * A variant of {@link #getCallIntent} for starting a video call. 120 */ getVideoCallIntent(String number, String callOrigin)121 public static Intent getVideoCallIntent(String number, String callOrigin) { 122 final Intent intent = new Intent(Intent.ACTION_CALL, getCallUri(number)); 123 intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, 124 VideoProfile.STATE_BIDIRECTIONAL); 125 if (!TextUtils.isEmpty(callOrigin)) { 126 intent.putExtra(PhoneConstants.EXTRA_CALL_ORIGIN, callOrigin); 127 } 128 return intent; 129 } 130 131 /** 132 * Return Uri with an appropriate scheme, accepting both SIP and usual phone call 133 * numbers. 134 */ getCallUri(String number)135 public static Uri getCallUri(String number) { 136 if (PhoneNumberHelper.isUriNumber(number)) { 137 return Uri.fromParts(PhoneAccount.SCHEME_SIP, number, null); 138 } 139 return Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null); 140 } 141 142 /** 143 * Determines if video calling is available, and if so whether presence checking is available 144 * as well. 145 * 146 * Returns a bitmask with {@link #VIDEO_CALLING_ENABLED} to indicate that video calling is 147 * available, and {@link #VIDEO_CALLING_PRESENCE} if presence indication is also available. 148 * 149 * @param context The context 150 * @return A bit-mask describing the current video capabilities. 151 */ getVideoCallingAvailability(Context context)152 public static int getVideoCallingAvailability(Context context) { 153 if (!PermissionsUtil.hasPermission(context, android.Manifest.permission.READ_PHONE_STATE) 154 || !CompatUtils.isVideoCompatible()) { 155 return VIDEO_CALLING_DISABLED; 156 } 157 TelecomManager telecommMgr = (TelecomManager) 158 context.getSystemService(Context.TELECOM_SERVICE); 159 if (telecommMgr == null) { 160 return VIDEO_CALLING_DISABLED; 161 } 162 163 try { 164 List<PhoneAccountHandle> accountHandles = telecommMgr.getCallCapablePhoneAccounts(); 165 for (PhoneAccountHandle accountHandle : accountHandles) { 166 PhoneAccount account = telecommMgr.getPhoneAccount(accountHandle); 167 if (account != null) { 168 if (account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) { 169 // Builds prior to N do not have presence support. 170 if (!CompatUtils.isVideoPresenceCompatible()) { 171 return VIDEO_CALLING_ENABLED; 172 } 173 174 int videoCapabilities = VIDEO_CALLING_ENABLED; 175 if (account.hasCapabilities(PhoneAccountSdkCompat 176 .CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE)) { 177 videoCapabilities |= VIDEO_CALLING_PRESENCE; 178 } 179 return videoCapabilities; 180 } 181 } 182 } 183 return VIDEO_CALLING_DISABLED; 184 } catch (SecurityException e) { 185 FeedbackHelper.sendFeedback(context, TAG, 186 "Security exception when getting call capable phone accounts", e); 187 return VIDEO_CALLING_DISABLED; 188 } 189 } 190 191 /** 192 * Determines if one of the call capable phone accounts defined supports calling with a subject 193 * specified. 194 * 195 * @param context The context. 196 * @return {@code true} if one of the call capable phone accounts supports calling with a 197 * subject specified, {@code false} otherwise. 198 */ isCallWithSubjectSupported(Context context)199 public static boolean isCallWithSubjectSupported(Context context) { 200 if (!PermissionsUtil.hasPermission(context, android.Manifest.permission.READ_PHONE_STATE) 201 || !CompatUtils.isCallSubjectCompatible()) { 202 return false; 203 } 204 TelecomManager telecommMgr = (TelecomManager) 205 context.getSystemService(Context.TELECOM_SERVICE); 206 if (telecommMgr == null) { 207 return false; 208 } 209 210 try { 211 List<PhoneAccountHandle> accountHandles = telecommMgr.getCallCapablePhoneAccounts(); 212 for (PhoneAccountHandle accountHandle : accountHandles) { 213 PhoneAccount account = telecommMgr.getPhoneAccount(accountHandle); 214 if (account != null && account.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT)) { 215 return true; 216 } 217 } 218 return false; 219 } catch (SecurityException e) { 220 FeedbackHelper.sendFeedback(context, TAG, 221 "Security exception when getting call capable phone accounts", e); 222 return false; 223 } 224 225 } 226 227 /** 228 * Determines if we're able to use Tachyon as a fallback for video calling. 229 * 230 * @param context The context. 231 * @return {@code true} if there exists a call capable phone account which supports using a 232 * fallback for video calling, the carrier configuration supports a fallback, and the 233 * experiment for using a fallback is enabled. Otherwise {@code false} is returned. 234 */ isTachyonEnabled(Context context)235 public static boolean isTachyonEnabled(Context context) { 236 // Need to be able to read phone state, and be on at least N to check PhoneAccount extras. 237 if (!PermissionsUtil.hasPermission(context, android.Manifest.permission.READ_PHONE_STATE) 238 || !CompatUtils.isNCompatible()) { 239 return false; 240 } 241 TelecomManager telecommMgr = (TelecomManager) 242 context.getSystemService(Context.TELECOM_SERVICE); 243 if (telecommMgr == null) { 244 return false; 245 } 246 try { 247 List<PhoneAccountHandle> accountHandles = telecommMgr.getCallCapablePhoneAccounts(); 248 for (PhoneAccountHandle accountHandle : accountHandles) { 249 PhoneAccount account = telecommMgr.getPhoneAccount(accountHandle); 250 if (account == null) { 251 continue; 252 } 253 // Check availability for the device config. 254 final Bundle accountExtras = account.getExtras(); 255 final boolean deviceEnabled = accountExtras != null && accountExtras.getBoolean( 256 EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK); 257 if (Log.isLoggable(TAG, Log.DEBUG)) { 258 Log.d(TAG, "Device video fallback config: " + deviceEnabled); 259 } 260 261 // Check availability from carrier config. 262 final PersistableBundle carrierConfig = context.getSystemService( 263 CarrierConfigManager.class).getConfig(); 264 final boolean carrierEnabled = 265 carrierConfig != null && carrierConfig.getBoolean( 266 CONFIG_ALLOW_VIDEO_CALLING_FALLBACK); 267 if (Log.isLoggable(TAG, Log.DEBUG)) { 268 Log.d(TAG, "Carrier video fallback config: " + carrierEnabled); 269 } 270 271 // Check experiment value. 272 final boolean experimentEnabled = Flags.getInstance().getBoolean( 273 Experiments.QUICK_CONTACT_VIDEO_CALL); 274 if (Log.isLoggable(TAG, Log.DEBUG)) { 275 Log.d(TAG, "Experiment video fallback config: " + experimentEnabled); 276 } 277 278 // All three checks above must be true to enable Tachyon calling. 279 return deviceEnabled && carrierEnabled && experimentEnabled; 280 } 281 return false; 282 } catch (SecurityException e) { 283 FeedbackHelper.sendFeedback(context, TAG, 284 "Security exception when getting call capable phone accounts", e); 285 return false; 286 } 287 } 288 } 289