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 package android.telecom.cts; 17 18 import android.app.Instrumentation; 19 import android.bluetooth.BluetoothDevice; 20 import android.content.ComponentName; 21 import android.content.ContentProviderOperation; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.pm.PackageManager; 25 import android.graphics.Color; 26 import android.net.Uri; 27 import android.os.Build; 28 import android.os.Bundle; 29 import android.os.Handler; 30 import android.os.Looper; 31 import android.os.Parcel; 32 import android.os.ParcelFileDescriptor; 33 import android.os.Process; 34 import android.os.SystemClock; 35 import android.os.UserManager; 36 import android.provider.ContactsContract; 37 import android.telecom.PhoneAccount; 38 import android.telecom.PhoneAccountHandle; 39 import android.telecom.TelecomManager; 40 41 import androidx.test.InstrumentationRegistry; 42 43 import junit.framework.TestCase; 44 45 import java.io.BufferedReader; 46 import java.io.FileInputStream; 47 import java.io.InputStream; 48 import java.io.InputStreamReader; 49 import java.nio.charset.StandardCharsets; 50 import java.util.ArrayList; 51 import java.util.Optional; 52 import java.util.concurrent.CountDownLatch; 53 import java.util.concurrent.TimeUnit; 54 import java.util.function.Predicate; 55 56 public class TestUtils { 57 static final String TAG = "TelecomCTSTests"; 58 static final boolean HAS_TELECOM = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; 59 static final long WAIT_FOR_STATE_CHANGE_TIMEOUT_MS = 10000; 60 static final long WAIT_FOR_CALL_ADDED_TIMEOUT_S = 15; 61 static final long WAIT_FOR_STATE_CHANGE_TIMEOUT_CALLBACK = 50; 62 static final long WAIT_FOR_PHONE_STATE_LISTENER_REGISTERED_TIMEOUT_S = 15; 63 static final long WAIT_FOR_PHONE_STATE_LISTENER_CALLBACK_TIMEOUT_S = 15; 64 static final boolean HAS_BLUETOOTH = hasBluetoothFeature(); 65 static final BluetoothDevice BLUETOOTH_DEVICE1 = makeBluetoothDevice("00:00:00:00:00:01"); 66 static final BluetoothDevice BLUETOOTH_DEVICE2 = makeBluetoothDevice("00:00:00:00:00:02"); 67 68 // Non-final to allow modification by tests not in this package (e.g. permission-related 69 // tests in the Telecom2 test package. 70 public static String PACKAGE = "android.telecom.cts"; 71 public static final String TEST_URI_SCHEME = "foobuzz"; 72 public static final String COMPONENT = "android.telecom.cts.CtsConnectionService"; 73 public static final String INCALL_COMPONENT = "android.telecom.cts/.MockInCallService"; 74 public static final String SELF_MANAGED_COMPONENT = 75 "android.telecom.cts.CtsSelfManagedConnectionService"; 76 public static final String REMOTE_COMPONENT = "android.telecom.cts.CtsRemoteConnectionService"; 77 public static final String ACCOUNT_ID_1 = "xtstest_CALL_PROVIDER_ID_1"; 78 public static final String ACCOUNT_ID_2 = "xtstest_CALL_PROVIDER_ID_2"; 79 public static final String ACCOUNT_ID_SIM = "sim_acct"; 80 public static final String ACCOUNT_ID_EMERGENCY = "xtstest_CALL_PROVIDER_EMERGENCY"; 81 public static final String EXTRA_PHONE_NUMBER = "android.telecom.cts.extra.PHONE_NUMBER"; 82 public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE = 83 new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID_1); 84 public static final PhoneAccountHandle TEST_SIM_PHONE_ACCOUNT_HANDLE = 85 new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID_SIM); 86 public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE_2 = 87 new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID_2); 88 public static final PhoneAccountHandle TEST_EMERGENCY_PHONE_ACCOUNT_HANDLE = 89 new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID_EMERGENCY); 90 public static final String DEFAULT_TEST_ACCOUNT_1_ID = "ctstest_DEFAULT_TEST_ID_1"; 91 public static final String DEFAULT_TEST_ACCOUNT_2_ID = "ctstest_DEFAULT_TEST_ID_2"; 92 public static final PhoneAccountHandle TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_1 = 93 new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), 94 DEFAULT_TEST_ACCOUNT_1_ID); 95 public static final PhoneAccountHandle TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_2 = 96 new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), 97 DEFAULT_TEST_ACCOUNT_2_ID); 98 public static final PhoneAccountHandle TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE = 99 new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), "handoverFrom"); 100 public static final PhoneAccountHandle TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE = 101 new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT), 102 "handoverTo"); 103 public static final String REMOTE_ACCOUNT_ID = "xtstest_REMOTE_CALL_PROVIDER_ID"; 104 public static final String SELF_MANAGED_ACCOUNT_ID_1 = "ctstest_SELF_MANAGED_ID_1"; 105 public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_1 = 106 new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT), 107 SELF_MANAGED_ACCOUNT_ID_1); 108 public static final String SELF_MANAGED_ACCOUNT_ID_2 = "ctstest_SELF_MANAGED_ID_2"; 109 public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_2 = 110 new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT), 111 SELF_MANAGED_ACCOUNT_ID_2); 112 public static final String SELF_MANAGED_ACCOUNT_ID_3 = "ctstest_SELF_MANAGED_ID_3"; 113 public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_3 = 114 new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT), 115 SELF_MANAGED_ACCOUNT_ID_3); 116 public static final String SELF_MANAGED_ACCOUNT_ID_4 = "ctstest_SELF_MANAGED_ID_4"; 117 public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_4 = 118 new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT), 119 SELF_MANAGED_ACCOUNT_ID_4); 120 121 public static final String ACCOUNT_LABEL = "CTSConnectionService"; 122 public static final String SIM_ACCOUNT_LABEL = "CTSConnectionServiceSim"; 123 public static final PhoneAccount TEST_PHONE_ACCOUNT = PhoneAccount.builder( 124 TEST_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL) 125 .setAddress(Uri.parse("tel:555-TEST")) 126 .setSubscriptionAddress(Uri.parse("tel:555-TEST")) 127 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER | 128 PhoneAccount.CAPABILITY_VIDEO_CALLING | 129 PhoneAccount.CAPABILITY_RTT | 130 PhoneAccount.CAPABILITY_CONNECTION_MANAGER | 131 PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS | 132 PhoneAccount.CAPABILITY_ADHOC_CONFERENCE_CALLING) 133 .setHighlightColor(Color.RED) 134 .setShortDescription(ACCOUNT_LABEL) 135 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) 136 .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL) 137 .build(); 138 139 public static final PhoneAccount TEST_SIM_PHONE_ACCOUNT = PhoneAccount.builder( 140 TEST_SIM_PHONE_ACCOUNT_HANDLE, SIM_ACCOUNT_LABEL) 141 .setAddress(Uri.parse("tel:555-TEST")) 142 .setSubscriptionAddress(Uri.parse("tel:555-TEST")) 143 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER | 144 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) 145 .setHighlightColor(Color.RED) 146 .setShortDescription(SIM_ACCOUNT_LABEL) 147 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) 148 .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL) 149 .build(); 150 151 public static final PhoneAccount TEST_PHONE_ACCOUNT_2 = PhoneAccount.builder( 152 TEST_PHONE_ACCOUNT_HANDLE_2, ACCOUNT_LABEL + "2") 153 .setAddress(Uri.parse("tel:555-TEST2")) 154 .setSubscriptionAddress(Uri.parse("tel:555-TEST2")) 155 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER | 156 PhoneAccount.CAPABILITY_VIDEO_CALLING | 157 PhoneAccount.CAPABILITY_RTT | 158 PhoneAccount.CAPABILITY_CONNECTION_MANAGER) 159 .setHighlightColor(Color.BLUE) 160 .setShortDescription(ACCOUNT_LABEL) 161 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) 162 .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL) 163 .build(); 164 165 public static final PhoneAccount TEST_DEFAULT_PHONE_ACCOUNT_1 = PhoneAccount.builder( 166 TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_1, "Default Test 1") 167 .setAddress(Uri.parse("foobuzz:testuri1")) 168 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) 169 .setHighlightColor(Color.RED) 170 .setShortDescription("Default Test 1") 171 .addSupportedUriScheme(TEST_URI_SCHEME) 172 .build(); 173 public static final PhoneAccount TEST_DEFAULT_PHONE_ACCOUNT_2 = PhoneAccount.builder( 174 TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_2, "Default Test 2") 175 .setAddress(Uri.parse("foobuzz:testuri2")) 176 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) 177 .setHighlightColor(Color.RED) 178 .setShortDescription("Default Test 2") 179 .addSupportedUriScheme(TEST_URI_SCHEME) 180 .build(); 181 private static final Bundle SUPPORTS_HANDOVER_FROM_EXTRAS = new Bundle(); 182 private static final Bundle SUPPORTS_HANDOVER_TO_EXTRAS = new Bundle(); 183 static { SUPPORTS_HANDOVER_FROM_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_FROM, true)184 SUPPORTS_HANDOVER_FROM_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_FROM, true); SUPPORTS_HANDOVER_TO_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_TO, true)185 SUPPORTS_HANDOVER_TO_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_TO, true); 186 } 187 public static final PhoneAccount TEST_PHONE_ACCOUNT_HANDOVER_SRC = PhoneAccount.builder( 188 TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL) 189 .setAddress(Uri.parse("tel:555-TEST")) 190 .setExtras(SUPPORTS_HANDOVER_FROM_EXTRAS) 191 .setSubscriptionAddress(Uri.parse("tel:555-TEST")) 192 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) 193 .setHighlightColor(Color.BLUE) 194 .setShortDescription(ACCOUNT_LABEL) 195 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) 196 .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL) 197 .build(); 198 public static final PhoneAccount TEST_PHONE_ACCOUNT_HANDOVER_DEST = PhoneAccount.builder( 199 TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL) 200 .setAddress(Uri.parse("tel:555-TEST")) 201 .setExtras(SUPPORTS_HANDOVER_TO_EXTRAS) 202 .setSubscriptionAddress(Uri.parse("tel:555-TEST")) 203 .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED) 204 .setHighlightColor(Color.MAGENTA) 205 .setShortDescription(ACCOUNT_LABEL) 206 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) 207 .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL) 208 .build(); 209 public static final String REMOTE_ACCOUNT_LABEL = "CTSRemoteConnectionService"; 210 public static final String SELF_MANAGED_ACCOUNT_LABEL = "android.telecom.cts"; 211 public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_3 = PhoneAccount.builder( 212 TEST_SELF_MANAGED_HANDLE_3, SELF_MANAGED_ACCOUNT_LABEL) 213 .setAddress(Uri.fromParts(TEST_URI_SCHEME, "test@test.com", null)) 214 .setSubscriptionAddress(Uri.fromParts(TEST_URI_SCHEME, "test@test.com", null)) 215 .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED | 216 PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING | 217 PhoneAccount.CAPABILITY_VIDEO_CALLING) 218 .setHighlightColor(Color.BLUE) 219 .setShortDescription(SELF_MANAGED_ACCOUNT_LABEL) 220 .addSupportedUriScheme(TEST_URI_SCHEME) 221 .build(); 222 public static final Bundle SELF_MANAGED_ACCOUNT_1_EXTRAS; 223 static { 224 SELF_MANAGED_ACCOUNT_1_EXTRAS = new Bundle(); SELF_MANAGED_ACCOUNT_1_EXTRAS.putBoolean( PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, false)225 SELF_MANAGED_ACCOUNT_1_EXTRAS.putBoolean( 226 PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, false); 227 } 228 public static final Bundle SELF_MANAGED_ACCOUNT_2_EXTRAS; 229 static { 230 SELF_MANAGED_ACCOUNT_2_EXTRAS = new Bundle(); SELF_MANAGED_ACCOUNT_2_EXTRAS.putBoolean(PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS, true)231 SELF_MANAGED_ACCOUNT_2_EXTRAS.putBoolean(PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS, true); 232 } 233 public static final Bundle SELF_MANAGED_ACCOUNT_4_EXTRAS; 234 static { 235 SELF_MANAGED_ACCOUNT_4_EXTRAS = new Bundle(); SELF_MANAGED_ACCOUNT_4_EXTRAS.putBoolean( PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, true)236 SELF_MANAGED_ACCOUNT_4_EXTRAS.putBoolean( 237 PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, true); 238 } 239 240 public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_2 = PhoneAccount.builder( 241 TEST_SELF_MANAGED_HANDLE_2, SELF_MANAGED_ACCOUNT_LABEL) 242 .setAddress(Uri.parse("sip:test@test.com")) 243 .setSubscriptionAddress(Uri.parse("sip:test@test.com")) 244 .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED | 245 PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING | 246 PhoneAccount.CAPABILITY_VIDEO_CALLING) 247 .setHighlightColor(Color.BLUE) 248 .setShortDescription(SELF_MANAGED_ACCOUNT_LABEL) 249 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) 250 .addSupportedUriScheme(PhoneAccount.SCHEME_SIP) 251 .setExtras(SELF_MANAGED_ACCOUNT_2_EXTRAS) 252 .build(); 253 public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_1 = PhoneAccount.builder( 254 TEST_SELF_MANAGED_HANDLE_1, SELF_MANAGED_ACCOUNT_LABEL) 255 .setAddress(Uri.parse("sip:test@test.com")) 256 .setSubscriptionAddress(Uri.parse("sip:test@test.com")) 257 .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED | 258 PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING | 259 PhoneAccount.CAPABILITY_VIDEO_CALLING) 260 .setHighlightColor(Color.BLUE) 261 .setShortDescription(SELF_MANAGED_ACCOUNT_LABEL) 262 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) 263 .addSupportedUriScheme(PhoneAccount.SCHEME_SIP) 264 .setExtras(SELF_MANAGED_ACCOUNT_1_EXTRAS) 265 .build(); 266 public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_4 = PhoneAccount.builder( 267 TEST_SELF_MANAGED_HANDLE_4, SELF_MANAGED_ACCOUNT_LABEL) 268 .setAddress(Uri.parse("sip:test@test.com")) 269 .setSubscriptionAddress(Uri.parse("sip:test@test.com")) 270 .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED | 271 PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING | 272 PhoneAccount.CAPABILITY_VIDEO_CALLING) 273 .setHighlightColor(Color.BLUE) 274 .setShortDescription(SELF_MANAGED_ACCOUNT_LABEL) 275 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) 276 .addSupportedUriScheme(PhoneAccount.SCHEME_SIP) 277 .setExtras(SELF_MANAGED_ACCOUNT_4_EXTRAS) 278 .build(); 279 280 /** 281 * See {@link TelecomManager#ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION} 282 */ 283 public static final String ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION_STRING = 284 "ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION "; 285 286 /** 287 * See {@link TelecomManager#ENABLE_GET_PHONE_ACCOUNT_PERMISSION_PROTECTION} 288 */ 289 public static final String ENABLE_GET_PHONE_ACCOUNT_PERMISSION_PROTECTION_STRING = 290 "ENABLE_GET_PHONE_ACCOUNT_PERMISSION_PROTECTION "; 291 292 private static final String COMMAND_SET_CALL_DIAGNOSTIC_SERVICE = 293 "telecom set-call-diagnostic-service "; 294 295 private static final String COMMAND_SET_DEFAULT_DIALER = "telecom set-default-dialer "; 296 297 private static final String COMMAND_GET_DEFAULT_DIALER = "telecom get-default-dialer"; 298 299 private static final String COMMAND_SET_SYSTEM_DIALER = "telecom set-system-dialer "; 300 301 private static final String COMMAND_GET_SYSTEM_DIALER = "telecom get-system-dialer"; 302 303 private static final String COMMAND_ENABLE = "telecom set-phone-account-enabled "; 304 305 private static final String COMMAND_SET_ACCT_SUGGESTION = 306 "telecom set-phone-acct-suggestion-component "; 307 308 private static final String COMMAND_REGISTER_SIM = "telecom register-sim-phone-account "; 309 310 private static final String COMMAND_SET_DEFAULT_PHONE_ACCOUNT = 311 "telecom set-user-selected-outgoing-phone-account "; 312 313 private static final String COMMAND_WAIT_ON_HANDLERS = "telecom wait-on-handlers"; 314 315 private static final String COMMAND_ADD_TEST_EMERGENCY_NUMBER = 316 "cmd phone emergency-number-test-mode -a "; 317 318 private static final String COMMAND_REMOVE_TEST_EMERGENCY_NUMBER = 319 "cmd phone emergency-number-test-mode -r "; 320 321 private static final String COMMAND_CLEAR_TEST_EMERGENCY_NUMBERS = 322 "cmd phone emergency-number-test-mode -c"; 323 324 private static final String COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_NAME_FILTER = 325 "telecom set-test-emergency-phone-account-package-filter "; 326 327 private static final String COMMAND_AM_COMPAT = "am compat "; 328 329 public static final String MERGE_CALLER_NAME = "calls-merged"; 330 public static final String SWAP_CALLER_NAME = "calls-swapped"; 331 shouldTestTelecom(Context context)332 public static boolean shouldTestTelecom(Context context) { 333 if (!HAS_TELECOM) { 334 return false; 335 } 336 final PackageManager pm = context.getPackageManager(); 337 return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) && 338 pm.hasSystemFeature(PackageManager.FEATURE_CONNECTION_SERVICE); 339 } 340 setCallDiagnosticService(Instrumentation instrumentation, String packageName)341 public static String setCallDiagnosticService(Instrumentation instrumentation, 342 String packageName) 343 throws Exception { 344 return executeShellCommand(instrumentation, COMMAND_SET_CALL_DIAGNOSTIC_SERVICE 345 + packageName); 346 } 347 setDefaultDialer(Instrumentation instrumentation, String packageName)348 public static String setDefaultDialer(Instrumentation instrumentation, String packageName) 349 throws Exception { 350 return executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_DIALER + packageName); 351 } 352 setSystemDialerOverride(Instrumentation instrumentation)353 public static String setSystemDialerOverride(Instrumentation instrumentation) throws Exception { 354 return executeShellCommand(instrumentation, COMMAND_SET_SYSTEM_DIALER + INCALL_COMPONENT); 355 } 356 clearSystemDialerOverride( Instrumentation instrumentation)357 public static String clearSystemDialerOverride( 358 Instrumentation instrumentation) throws Exception { 359 return executeShellCommand(instrumentation, COMMAND_SET_SYSTEM_DIALER + "default"); 360 } 361 setCtsPhoneAccountSuggestionService(Instrumentation instrumentation, ComponentName componentName)362 public static String setCtsPhoneAccountSuggestionService(Instrumentation instrumentation, 363 ComponentName componentName) throws Exception { 364 return executeShellCommand(instrumentation, 365 COMMAND_SET_ACCT_SUGGESTION 366 + (componentName == null ? "" : componentName.flattenToString())); 367 } 368 getDefaultDialer(Instrumentation instrumentation)369 public static String getDefaultDialer(Instrumentation instrumentation) throws Exception { 370 return executeShellCommand(instrumentation, COMMAND_GET_DEFAULT_DIALER); 371 } 372 getSystemDialer(Instrumentation instrumentation)373 public static String getSystemDialer(Instrumentation instrumentation) throws Exception { 374 return executeShellCommand(instrumentation, COMMAND_GET_SYSTEM_DIALER); 375 } 376 enablePhoneAccount(Instrumentation instrumentation, PhoneAccountHandle handle)377 public static void enablePhoneAccount(Instrumentation instrumentation, 378 PhoneAccountHandle handle) throws Exception { 379 final ComponentName component = handle.getComponentName(); 380 final long currentUserSerial = getCurrentUserSerialNumber(instrumentation); 381 executeShellCommand(instrumentation, COMMAND_ENABLE 382 + component.getPackageName() + "/" + component.getClassName() + " " 383 + handle.getId() + " " + currentUserSerial); 384 } 385 registerSimPhoneAccount(Instrumentation instrumentation, PhoneAccountHandle handle, String label, String address)386 public static void registerSimPhoneAccount(Instrumentation instrumentation, 387 PhoneAccountHandle handle, String label, String address) throws Exception { 388 final ComponentName component = handle.getComponentName(); 389 final long currentUserSerial = getCurrentUserSerialNumber(instrumentation); 390 executeShellCommand(instrumentation, COMMAND_REGISTER_SIM 391 + component.getPackageName() + "/" + component.getClassName() + " " 392 + handle.getId() + " " + currentUserSerial + " " + label + " " + address); 393 } 394 registerEmergencyPhoneAccount(Instrumentation instrumentation, PhoneAccountHandle handle, String label, String address)395 public static void registerEmergencyPhoneAccount(Instrumentation instrumentation, 396 PhoneAccountHandle handle, String label, String address) throws Exception { 397 final ComponentName component = handle.getComponentName(); 398 final long currentUserSerial = getCurrentUserSerialNumber(instrumentation); 399 executeShellCommand(instrumentation, COMMAND_REGISTER_SIM + "-e " 400 + component.getPackageName() + "/" + component.getClassName() + " " 401 + handle.getId() + " " + currentUserSerial + " " + label + " " + address); 402 } 403 setDefaultOutgoingPhoneAccount(Instrumentation instrumentation, PhoneAccountHandle handle)404 public static void setDefaultOutgoingPhoneAccount(Instrumentation instrumentation, 405 PhoneAccountHandle handle) throws Exception { 406 if (handle != null) { 407 final ComponentName component = handle.getComponentName(); 408 final long currentUserSerial = getCurrentUserSerialNumber(instrumentation); 409 executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_PHONE_ACCOUNT 410 + component.getPackageName() + "/" + component.getClassName() + " " 411 + handle.getId() + " " + currentUserSerial); 412 } else { 413 executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_PHONE_ACCOUNT); 414 } 415 } 416 waitOnAllHandlers(Instrumentation instrumentation)417 public static void waitOnAllHandlers(Instrumentation instrumentation) { 418 try { 419 executeShellCommand(instrumentation, COMMAND_WAIT_ON_HANDLERS); 420 } catch (Throwable t) { 421 throw new RuntimeException(t); 422 } 423 } 424 waitOnLocalMainLooper(long timeoutMs)425 public static void waitOnLocalMainLooper(long timeoutMs) { 426 Handler mainHandler = new Handler(Looper.getMainLooper()); 427 final CountDownLatch lock = new CountDownLatch(1); 428 mainHandler.post(lock::countDown); 429 while (lock.getCount() > 0) { 430 try { 431 lock.await(timeoutMs, TimeUnit.MILLISECONDS); 432 } catch (InterruptedException e) { 433 // do nothing 434 } 435 } 436 } 437 addTestEmergencyNumber(Instrumentation instr, String testNumber)438 public static void addTestEmergencyNumber(Instrumentation instr, 439 String testNumber) throws Exception { 440 executeShellCommand(instr, COMMAND_ADD_TEST_EMERGENCY_NUMBER + testNumber); 441 } 442 removeTestEmergencyNumber(Instrumentation instr, String number)443 public static void removeTestEmergencyNumber(Instrumentation instr, 444 String number) throws Exception { 445 executeShellCommand(instr, COMMAND_REMOVE_TEST_EMERGENCY_NUMBER + number); 446 } 447 clearTestEmergencyNumbers(Instrumentation instr)448 public static void clearTestEmergencyNumbers(Instrumentation instr) throws Exception { 449 executeShellCommand(instr, COMMAND_CLEAR_TEST_EMERGENCY_NUMBERS); 450 } 451 setTestEmergencyPhoneAccountPackageFilter(Instrumentation instr, Context context)452 public static void setTestEmergencyPhoneAccountPackageFilter(Instrumentation instr, 453 Context context) throws Exception { 454 executeShellCommand(instr, COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_NAME_FILTER 455 + context.getPackageName()); 456 } 457 clearTestEmergencyPhoneAccountPackageFilter( Instrumentation instr)458 public static void clearTestEmergencyPhoneAccountPackageFilter( 459 Instrumentation instr) throws Exception { 460 executeShellCommand(instr, COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_NAME_FILTER); 461 } 462 enableCompatCommand(Instrumentation instr, String commandName)463 public static void enableCompatCommand(Instrumentation instr, 464 String commandName) throws Exception { 465 String cmd = COMMAND_AM_COMPAT + "enable --no-kill " + commandName + PACKAGE; 466 executeShellCommand(instr, cmd); 467 } 468 disableCompatCommand(Instrumentation instr, String commandName)469 public static void disableCompatCommand(Instrumentation instr, 470 String commandName) throws Exception { 471 String cmd = COMMAND_AM_COMPAT + "disable --no-kill " + commandName + PACKAGE; 472 executeShellCommand(instr, cmd); 473 } 474 resetCompatCommand(Instrumentation instr, String commandName)475 public static void resetCompatCommand(Instrumentation instr, 476 String commandName) throws Exception { 477 String cmd = COMMAND_AM_COMPAT + "reset --no-kill " + commandName + PACKAGE; 478 executeShellCommand(instr, cmd); 479 } 480 481 /** 482 * Executes the given shell command and returns the output in a string. Note that even 483 * if we don't care about the output, we have to read the stream completely to make the 484 * command execute. 485 */ executeShellCommand(Instrumentation instrumentation, String command)486 public static String executeShellCommand(Instrumentation instrumentation, 487 String command) throws Exception { 488 final ParcelFileDescriptor pfd = 489 instrumentation.getUiAutomation().executeShellCommand(command); 490 BufferedReader br = null; 491 try (InputStream in = new FileInputStream(pfd.getFileDescriptor())) { 492 br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); 493 String str = null; 494 StringBuilder out = new StringBuilder(); 495 while ((str = br.readLine()) != null) { 496 out.append(str); 497 } 498 return out.toString(); 499 } finally { 500 if (br != null) { 501 closeQuietly(br); 502 } 503 closeQuietly(pfd); 504 } 505 } 506 closeQuietly(AutoCloseable closeable)507 private static void closeQuietly(AutoCloseable closeable) { 508 if (closeable != null) { 509 try { 510 closeable.close(); 511 } catch (RuntimeException rethrown) { 512 throw rethrown; 513 } catch (Exception ignored) { 514 } 515 } 516 } 517 518 /** 519 * Waits for the {@link CountDownLatch} to count down to 0 and then returns without reseting 520 * the latch. 521 * @param lock the latch that the system will wait on. 522 * @return true if the latch was released successfully, false if the latch timed out before 523 * resetting. 524 */ waitForLatchCountDown(CountDownLatch lock)525 public static boolean waitForLatchCountDown(CountDownLatch lock) { 526 if (lock == null) { 527 return false; 528 } 529 530 boolean success; 531 try { 532 success = lock.await(5000, TimeUnit.MILLISECONDS); 533 } catch (InterruptedException ie) { 534 return false; 535 } 536 537 return success; 538 } 539 540 /** 541 * Waits for the {@link CountDownLatch} to count down to 0 and then returns a new reset latch. 542 * @param lock The lock that will await a countDown to 0. 543 * @return a new reset {@link CountDownLatch} if the lock successfully counted down to 0 or 544 * null if the operation timed out. 545 */ waitForLock(CountDownLatch lock)546 public static CountDownLatch waitForLock(CountDownLatch lock) { 547 boolean success = waitForLatchCountDown(lock); 548 if (success) { 549 return new CountDownLatch(1); 550 } else { 551 return null; 552 } 553 } 554 555 /** 556 * Adds a new incoming call. 557 * 558 * @param instrumentation the Instrumentation, used for shell command execution. 559 * @param telecomManager the TelecomManager. 560 * @param handle the PhoneAccountHandle associated with the call. 561 * @param address the incoming address. 562 * @return the new self-managed incoming call. 563 */ addIncomingCall(Instrumentation instrumentation, TelecomManager telecomManager, PhoneAccountHandle handle, Uri address)564 public static void addIncomingCall(Instrumentation instrumentation, 565 TelecomManager telecomManager, PhoneAccountHandle handle, 566 Uri address) { 567 568 // Inform telecom of new incoming self-managed connection. 569 Bundle extras = new Bundle(); 570 extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, address); 571 telecomManager.addNewIncomingCall(handle, extras); 572 573 // Wait for Telecom to finish creating the new connection. 574 try { 575 waitOnAllHandlers(instrumentation); 576 } catch (Exception e) { 577 TestCase.fail("Failed to wait on handlers"); 578 } 579 } hasBluetoothFeature()580 public static boolean hasBluetoothFeature() { 581 return InstrumentationRegistry.getContext().getPackageManager(). 582 hasSystemFeature(PackageManager.FEATURE_BLUETOOTH); 583 } makeBluetoothDevice(String address)584 public static BluetoothDevice makeBluetoothDevice(String address) { 585 if (!HAS_BLUETOOTH) return null; 586 Parcel p1 = Parcel.obtain(); 587 p1.writeString(address); 588 p1.setDataPosition(0); 589 BluetoothDevice device = BluetoothDevice.CREATOR.createFromParcel(p1); 590 p1.recycle(); 591 return device; 592 } 593 594 /** 595 * Places a new outgoing call. 596 * 597 * @param telecomManager the TelecomManager. 598 * @param handle the PhoneAccountHandle associated with the call. 599 * @param address outgoing call address. 600 * @return the new self-managed outgoing call. 601 */ placeOutgoingCall(Instrumentation instrumentation, TelecomManager telecomManager, PhoneAccountHandle handle, Uri address)602 public static void placeOutgoingCall(Instrumentation instrumentation, 603 TelecomManager telecomManager, PhoneAccountHandle handle, 604 Uri address) { 605 // Inform telecom of new incoming self-managed connection. 606 Bundle extras = new Bundle(); 607 extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, handle); 608 telecomManager.placeCall(address, extras); 609 610 // Wait for Telecom to finish creating the new connection. 611 try { 612 waitOnAllHandlers(instrumentation); 613 } catch (Exception e) { 614 TestCase.fail("Failed to wait on handlers"); 615 } 616 } 617 618 /** 619 * Waits for a new SelfManagedConnection with the given address to be added. 620 * @param address The expected address. 621 * @return The SelfManagedConnection found. 622 */ waitForAndGetConnection(Uri address)623 public static SelfManagedConnection waitForAndGetConnection(Uri address) { 624 // Wait for creation of the new connection. 625 if (!CtsSelfManagedConnectionService.waitForBinding()) { 626 TestCase.fail("Could not bind to Self-Managed ConnectionService"); 627 } 628 CtsSelfManagedConnectionService connectionService = 629 CtsSelfManagedConnectionService.getConnectionService(); 630 TestCase.assertTrue(connectionService.waitForUpdate( 631 CtsSelfManagedConnectionService.CONNECTION_CREATED_LOCK)); 632 633 Optional<SelfManagedConnection> connectionOptional = connectionService.getConnections() 634 .stream() 635 .filter(connection -> address.equals(connection.getAddress())) 636 .findFirst(); 637 assert(connectionOptional.isPresent()); 638 return connectionOptional.get(); 639 } 640 641 /** 642 * Utility class used to track the number of times a callback was invoked, and the arguments it 643 * was invoked with. This class is prefixed Invoke rather than the more typical Call for 644 * disambiguation purposes. 645 */ 646 public static final class InvokeCounter { 647 private final String mName; 648 private final Object mLock = new Object(); 649 private final ArrayList<Object[]> mInvokeArgs = new ArrayList<>(); 650 651 private int mInvokeCount; 652 InvokeCounter(String callbackName)653 public InvokeCounter(String callbackName) { 654 mName = callbackName; 655 } 656 invoke(Object... args)657 public void invoke(Object... args) { 658 synchronized (mLock) { 659 mInvokeCount++; 660 mInvokeArgs.add(args); 661 mLock.notifyAll(); 662 } 663 } 664 getArgs(int index)665 public Object[] getArgs(int index) { 666 synchronized (mLock) { 667 return mInvokeArgs.get(index); 668 } 669 } 670 getInvokeCount()671 public int getInvokeCount() { 672 synchronized (mLock) { 673 return mInvokeCount; 674 } 675 } 676 waitForCount(int count)677 public void waitForCount(int count) { 678 waitForCount(count, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS); 679 } 680 waitForCount(int count, long timeoutMillis)681 public void waitForCount(int count, long timeoutMillis) { 682 waitForCount(count, timeoutMillis, null); 683 } 684 waitForCount(long timeoutMillis)685 public void waitForCount(long timeoutMillis) { 686 synchronized (mLock) { 687 try { 688 mLock.wait(timeoutMillis); 689 }catch (InterruptedException ex) { 690 ex.printStackTrace(); 691 } 692 } 693 } 694 waitForCount(int count, long timeoutMillis, String message)695 public void waitForCount(int count, long timeoutMillis, String message) { 696 synchronized (mLock) { 697 final long startTimeMillis = SystemClock.uptimeMillis(); 698 while (mInvokeCount < count) { 699 try { 700 final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; 701 final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis; 702 if (remainingTimeMillis <= 0) { 703 if (message != null) { 704 TestCase.fail(message); 705 } else { 706 TestCase.fail(String.format("Expected %s to be called %d times.", 707 mName, count)); 708 } 709 } 710 mLock.wait(timeoutMillis); 711 } catch (InterruptedException ie) { 712 /* ignore */ 713 } 714 } 715 } 716 } 717 718 /** 719 * Waits for a predicate to return {@code true} within the specified timeout. Uses the 720 * {@link #mLock} for this {@link InvokeCounter} to eliminate the need to perform busy-wait. 721 * @param predicate The predicate. 722 * @param timeoutMillis The timeout. 723 */ waitForPredicate(Predicate predicate, long timeoutMillis)724 public void waitForPredicate(Predicate predicate, long timeoutMillis) { 725 synchronized (mLock) { 726 long startTimeMillis = SystemClock.uptimeMillis(); 727 long elapsedTimeMillis = 0; 728 long remainingTimeMillis = timeoutMillis; 729 Object foundValue = null; 730 boolean wasFound = false; 731 do { 732 try { 733 mLock.wait(timeoutMillis); 734 foundValue = (mInvokeArgs.get(mInvokeArgs.size()-1))[0]; 735 wasFound = predicate.test(foundValue); 736 elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; 737 remainingTimeMillis = timeoutMillis - elapsedTimeMillis; 738 } catch (InterruptedException ie) { 739 /* ignore */ 740 } 741 } while (!wasFound && remainingTimeMillis > 0); 742 if (wasFound) { 743 return; 744 } else if (remainingTimeMillis <= 0) { 745 TestCase.fail("Expected value not found within time limit"); 746 } 747 } 748 } 749 clearArgs()750 public void clearArgs() { 751 synchronized (mLock) { 752 mInvokeArgs.clear(); 753 } 754 } 755 reset()756 public void reset() { 757 synchronized (mLock) { 758 clearArgs(); 759 mInvokeCount = 0; 760 } 761 } 762 } 763 getCurrentUserSerialNumber(Instrumentation instrumentation)764 private static long getCurrentUserSerialNumber(Instrumentation instrumentation) { 765 UserManager userManager = 766 instrumentation.getContext().getSystemService(UserManager.class); 767 return userManager.getSerialNumberForUser(Process.myUserHandle()); 768 } 769 770 771 insertContact(ContentResolver contentResolver, String phoneNumber)772 public static Uri insertContact(ContentResolver contentResolver, String phoneNumber) 773 throws Exception { 774 ArrayList<ContentProviderOperation> ops = new ArrayList<>(); 775 ops.add(ContentProviderOperation 776 .newInsert(ContactsContract.RawContacts.CONTENT_URI) 777 .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, "test_type") 778 .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, "test_name") 779 .build()); 780 ops.add(ContentProviderOperation 781 .newInsert(ContactsContract.Data.CONTENT_URI) 782 .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) 783 .withValue(ContactsContract.Data.MIMETYPE, 784 ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) 785 .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, "test") 786 .build()); 787 ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) 788 .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) 789 .withValue(ContactsContract.Data.MIMETYPE, 790 ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) 791 .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phoneNumber) 792 .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, 793 ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE) 794 .withYieldAllowed(true) 795 .build()); 796 return contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)[0].uri; 797 } 798 deleteContact(ContentResolver contentResolver, Uri deleteUri)799 public static int deleteContact(ContentResolver contentResolver, Uri deleteUri) { 800 return contentResolver.delete(deleteUri, null, null); 801 } 802 } 803