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