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.Context; 22 import android.content.pm.PackageManager; 23 import android.graphics.Color; 24 import android.net.Uri; 25 import android.os.Build; 26 import android.os.Bundle; 27 import android.os.Handler; 28 import android.os.Looper; 29 import android.os.Parcel; 30 import android.os.ParcelFileDescriptor; 31 import android.os.Process; 32 import android.os.SystemClock; 33 import android.os.UserManager; 34 import android.telecom.PhoneAccount; 35 import android.telecom.PhoneAccountHandle; 36 import android.telecom.TelecomManager; 37 38 import androidx.test.InstrumentationRegistry; 39 40 import junit.framework.TestCase; 41 42 import java.io.BufferedReader; 43 import java.io.FileInputStream; 44 import java.io.InputStream; 45 import java.io.InputStreamReader; 46 import java.nio.charset.StandardCharsets; 47 import java.util.ArrayList; 48 import java.util.Optional; 49 import java.util.concurrent.CountDownLatch; 50 import java.util.concurrent.TimeUnit; 51 import java.util.function.Predicate; 52 53 public class TestUtils { 54 static final String TAG = "TelecomCTSTests"; 55 static final boolean HAS_TELECOM = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; 56 static final long WAIT_FOR_STATE_CHANGE_TIMEOUT_MS = 10000; 57 static final long WAIT_FOR_CALL_ADDED_TIMEOUT_S = 15; 58 static final long WAIT_FOR_STATE_CHANGE_TIMEOUT_CALLBACK = 50; 59 static final long WAIT_FOR_PHONE_STATE_LISTENER_REGISTERED_TIMEOUT_S = 15; 60 static final long WAIT_FOR_PHONE_STATE_LISTENER_CALLBACK_TIMEOUT_S = 15; 61 static final boolean HAS_BLUETOOTH = hasBluetoothFeature(); 62 static final BluetoothDevice BLUETOOTH_DEVICE1 = makeBluetoothDevice("00:00:00:00:00:01"); 63 static final BluetoothDevice BLUETOOTH_DEVICE2 = makeBluetoothDevice("00:00:00:00:00:02"); 64 65 // Non-final to allow modification by tests not in this package (e.g. permission-related 66 // tests in the Telecom2 test package. 67 public static String PACKAGE = "android.telecom.cts"; 68 public static final String TEST_URI_SCHEME = "foobuzz"; 69 public static final String COMPONENT = "android.telecom.cts.CtsConnectionService"; 70 public static final String SELF_MANAGED_COMPONENT = 71 "android.telecom.cts.CtsSelfManagedConnectionService"; 72 public static final String REMOTE_COMPONENT = "android.telecom.cts.CtsRemoteConnectionService"; 73 public static final String ACCOUNT_ID_1 = "xtstest_CALL_PROVIDER_ID_1"; 74 public static final String ACCOUNT_ID_2 = "xtstest_CALL_PROVIDER_ID_2"; 75 public static final String EXTRA_PHONE_NUMBER = "android.telecom.cts.extra.PHONE_NUMBER"; 76 public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE = 77 new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID_1); 78 public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE_2 = 79 new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), ACCOUNT_ID_2); 80 public static final String DEFAULT_TEST_ACCOUNT_1_ID = "ctstest_DEFAULT_TEST_ID_1"; 81 public static final String DEFAULT_TEST_ACCOUNT_2_ID = "ctstest_DEFAULT_TEST_ID_2"; 82 public static final PhoneAccountHandle TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_1 = 83 new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), 84 DEFAULT_TEST_ACCOUNT_1_ID); 85 public static final PhoneAccountHandle TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_2 = 86 new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), 87 DEFAULT_TEST_ACCOUNT_2_ID); 88 public static final PhoneAccountHandle TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE = 89 new PhoneAccountHandle(new ComponentName(PACKAGE, COMPONENT), "handoverFrom"); 90 public static final PhoneAccountHandle TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE = 91 new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT), 92 "handoverTo"); 93 public static final String REMOTE_ACCOUNT_ID = "xtstest_REMOTE_CALL_PROVIDER_ID"; 94 public static final String SELF_MANAGED_ACCOUNT_ID_1 = "ctstest_SELF_MANAGED_ID_1"; 95 public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_1 = 96 new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT), 97 SELF_MANAGED_ACCOUNT_ID_1); 98 public static final String SELF_MANAGED_ACCOUNT_ID_2 = "ctstest_SELF_MANAGED_ID_2"; 99 public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_2 = 100 new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT), 101 SELF_MANAGED_ACCOUNT_ID_2); 102 public static final String SELF_MANAGED_ACCOUNT_ID_3 = "ctstest_SELF_MANAGED_ID_3"; 103 public static final PhoneAccountHandle TEST_SELF_MANAGED_HANDLE_3 = 104 new PhoneAccountHandle(new ComponentName(PACKAGE, SELF_MANAGED_COMPONENT), 105 SELF_MANAGED_ACCOUNT_ID_3); 106 107 public static final String ACCOUNT_LABEL = "CTSConnectionService"; 108 public static final PhoneAccount TEST_PHONE_ACCOUNT = PhoneAccount.builder( 109 TEST_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL) 110 .setAddress(Uri.parse("tel:555-TEST")) 111 .setSubscriptionAddress(Uri.parse("tel:555-TEST")) 112 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER | 113 PhoneAccount.CAPABILITY_VIDEO_CALLING | 114 PhoneAccount.CAPABILITY_RTT | 115 PhoneAccount.CAPABILITY_CONNECTION_MANAGER) 116 .setHighlightColor(Color.RED) 117 .setShortDescription(ACCOUNT_LABEL) 118 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) 119 .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL) 120 .build(); 121 122 public static final PhoneAccount TEST_PHONE_ACCOUNT_2 = PhoneAccount.builder( 123 TEST_PHONE_ACCOUNT_HANDLE_2, ACCOUNT_LABEL + "2") 124 .setAddress(Uri.parse("tel:555-TEST2")) 125 .setSubscriptionAddress(Uri.parse("tel:555-TEST2")) 126 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER | 127 PhoneAccount.CAPABILITY_VIDEO_CALLING | 128 PhoneAccount.CAPABILITY_RTT | 129 PhoneAccount.CAPABILITY_CONNECTION_MANAGER) 130 .setHighlightColor(Color.BLUE) 131 .setShortDescription(ACCOUNT_LABEL) 132 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) 133 .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL) 134 .build(); 135 136 public static final PhoneAccount TEST_DEFAULT_PHONE_ACCOUNT_1 = PhoneAccount.builder( 137 TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_1, "Default Test 1") 138 .setAddress(Uri.parse("foobuzz:testuri1")) 139 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) 140 .setHighlightColor(Color.RED) 141 .setShortDescription("Default Test 1") 142 .addSupportedUriScheme(TEST_URI_SCHEME) 143 .build(); 144 public static final PhoneAccount TEST_DEFAULT_PHONE_ACCOUNT_2 = PhoneAccount.builder( 145 TEST_DEFAULT_PHONE_ACCOUNT_HANDLE_2, "Default Test 2") 146 .setAddress(Uri.parse("foobuzz:testuri2")) 147 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) 148 .setHighlightColor(Color.RED) 149 .setShortDescription("Default Test 2") 150 .addSupportedUriScheme(TEST_URI_SCHEME) 151 .build(); 152 private static final Bundle SUPPORTS_HANDOVER_FROM_EXTRAS = new Bundle(); 153 private static final Bundle SUPPORTS_HANDOVER_TO_EXTRAS = new Bundle(); 154 static { 155 SUPPORTS_HANDOVER_FROM_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_FROM, true); 156 SUPPORTS_HANDOVER_TO_EXTRAS.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_TO, true); 157 } 158 public static final PhoneAccount TEST_PHONE_ACCOUNT_HANDOVER_SRC = PhoneAccount.builder( 159 TEST_HANDOVER_SRC_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL) 160 .setAddress(Uri.parse("tel:555-TEST")) 161 .setExtras(SUPPORTS_HANDOVER_FROM_EXTRAS) 162 .setSubscriptionAddress(Uri.parse("tel:555-TEST")) 163 .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER) 164 .setHighlightColor(Color.BLUE) 165 .setShortDescription(ACCOUNT_LABEL) 166 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) 167 .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL) 168 .build(); 169 public static final PhoneAccount TEST_PHONE_ACCOUNT_HANDOVER_DEST = PhoneAccount.builder( 170 TEST_HANDOVER_DEST_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL) 171 .setAddress(Uri.parse("tel:555-TEST")) 172 .setExtras(SUPPORTS_HANDOVER_TO_EXTRAS) 173 .setSubscriptionAddress(Uri.parse("tel:555-TEST")) 174 .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED) 175 .setHighlightColor(Color.MAGENTA) 176 .setShortDescription(ACCOUNT_LABEL) 177 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) 178 .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL) 179 .build(); 180 public static final String REMOTE_ACCOUNT_LABEL = "CTSRemoteConnectionService"; 181 public static final String SELF_MANAGED_ACCOUNT_LABEL = "android.telecom.cts"; 182 public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_3 = PhoneAccount.builder( 183 TEST_SELF_MANAGED_HANDLE_3, SELF_MANAGED_ACCOUNT_LABEL) 184 .setAddress(Uri.fromParts(TEST_URI_SCHEME, "test (at) test.com", null)) 185 .setSubscriptionAddress(Uri.fromParts(TEST_URI_SCHEME, "test (at) test.com", null)) 186 .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED | 187 PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING | 188 PhoneAccount.CAPABILITY_VIDEO_CALLING) 189 .setHighlightColor(Color.BLUE) 190 .setShortDescription(SELF_MANAGED_ACCOUNT_LABEL) 191 .addSupportedUriScheme(TEST_URI_SCHEME) 192 .build(); 193 public static final Bundle SELF_MANAGED_ACCOUNT_2_EXTRAS; 194 static { 195 SELF_MANAGED_ACCOUNT_2_EXTRAS = new Bundle(); 196 SELF_MANAGED_ACCOUNT_2_EXTRAS.putBoolean(PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS, true); 197 } 198 199 public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_2 = PhoneAccount.builder( 200 TEST_SELF_MANAGED_HANDLE_2, SELF_MANAGED_ACCOUNT_LABEL) 201 .setAddress(Uri.parse("sip:test (at) test.com")) 202 .setSubscriptionAddress(Uri.parse("sip:test (at) test.com")) 203 .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED | 204 PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING | 205 PhoneAccount.CAPABILITY_VIDEO_CALLING) 206 .setHighlightColor(Color.BLUE) 207 .setShortDescription(SELF_MANAGED_ACCOUNT_LABEL) 208 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) 209 .addSupportedUriScheme(PhoneAccount.SCHEME_SIP) 210 .setExtras(SELF_MANAGED_ACCOUNT_2_EXTRAS) 211 .build(); 212 public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_1 = PhoneAccount.builder( 213 TEST_SELF_MANAGED_HANDLE_1, SELF_MANAGED_ACCOUNT_LABEL) 214 .setAddress(Uri.parse("sip:test (at) test.com")) 215 .setSubscriptionAddress(Uri.parse("sip:test (at) test.com")) 216 .setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED | 217 PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING | 218 PhoneAccount.CAPABILITY_VIDEO_CALLING) 219 .setHighlightColor(Color.BLUE) 220 .setShortDescription(SELF_MANAGED_ACCOUNT_LABEL) 221 .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) 222 .addSupportedUriScheme(PhoneAccount.SCHEME_SIP) 223 .build(); 224 225 private static final String COMMAND_SET_DEFAULT_DIALER = "telecom set-default-dialer "; 226 227 private static final String COMMAND_GET_DEFAULT_DIALER = "telecom get-default-dialer"; 228 229 private static final String COMMAND_GET_SYSTEM_DIALER = "telecom get-system-dialer"; 230 231 private static final String COMMAND_ENABLE = "telecom set-phone-account-enabled "; 232 233 private static final String COMMAND_SET_ACCT_SUGGESTION = 234 "telecom set-phone-acct-suggestion-component "; 235 236 private static final String COMMAND_REGISTER_SIM = "telecom register-sim-phone-account "; 237 238 private static final String COMMAND_SET_DEFAULT_PHONE_ACCOUNT = 239 "telecom set-user-selected-outgoing-phone-account "; 240 241 private static final String COMMAND_WAIT_ON_HANDLERS = "telecom wait-on-handlers"; 242 243 public static final String MERGE_CALLER_NAME = "calls-merged"; 244 public static final String SWAP_CALLER_NAME = "calls-swapped"; 245 246 public static boolean shouldTestTelecom(Context context) { 247 if (!HAS_TELECOM) { 248 return false; 249 } 250 final PackageManager pm = context.getPackageManager(); 251 return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) && 252 pm.hasSystemFeature(PackageManager.FEATURE_CONNECTION_SERVICE); 253 } 254 255 public static String setDefaultDialer(Instrumentation instrumentation, String packageName) 256 throws Exception { 257 return executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_DIALER + packageName); 258 } 259 260 public static String setCtsPhoneAccountSuggestionService(Instrumentation instrumentation, 261 ComponentName componentName) throws Exception { 262 return executeShellCommand(instrumentation, 263 COMMAND_SET_ACCT_SUGGESTION 264 + (componentName == null ? "" : componentName.flattenToString())); 265 } 266 267 public static String getDefaultDialer(Instrumentation instrumentation) throws Exception { 268 return executeShellCommand(instrumentation, COMMAND_GET_DEFAULT_DIALER); 269 } 270 271 public static String getSystemDialer(Instrumentation instrumentation) throws Exception { 272 return executeShellCommand(instrumentation, COMMAND_GET_SYSTEM_DIALER); 273 } 274 275 public static void enablePhoneAccount(Instrumentation instrumentation, 276 PhoneAccountHandle handle) throws Exception { 277 final ComponentName component = handle.getComponentName(); 278 final long currentUserSerial = getCurrentUserSerialNumber(instrumentation); 279 executeShellCommand(instrumentation, COMMAND_ENABLE 280 + component.getPackageName() + "/" + component.getClassName() + " " 281 + handle.getId() + " " + currentUserSerial); 282 } 283 284 public static void registerSimPhoneAccount(Instrumentation instrumentation, 285 PhoneAccountHandle handle, String label, String address) throws Exception { 286 final ComponentName component = handle.getComponentName(); 287 final long currentUserSerial = getCurrentUserSerialNumber(instrumentation); 288 executeShellCommand(instrumentation, COMMAND_REGISTER_SIM 289 + component.getPackageName() + "/" + component.getClassName() + " " 290 + handle.getId() + " " + currentUserSerial + " " + label + " " + address); 291 } 292 293 public static void setDefaultOutgoingPhoneAccount(Instrumentation instrumentation, 294 PhoneAccountHandle handle) throws Exception { 295 if (handle != null) { 296 final ComponentName component = handle.getComponentName(); 297 final long currentUserSerial = getCurrentUserSerialNumber(instrumentation); 298 executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_PHONE_ACCOUNT 299 + component.getPackageName() + "/" + component.getClassName() + " " 300 + handle.getId() + " " + currentUserSerial); 301 } else { 302 executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_PHONE_ACCOUNT); 303 } 304 } 305 306 public static void waitOnAllHandlers(Instrumentation instrumentation) throws Exception { 307 executeShellCommand(instrumentation, COMMAND_WAIT_ON_HANDLERS); 308 } 309 310 public static void waitOnLocalMainLooper(long timeoutMs) { 311 Handler mainHandler = new Handler(Looper.getMainLooper()); 312 final CountDownLatch lock = new CountDownLatch(1); 313 mainHandler.post(lock::countDown); 314 while (lock.getCount() > 0) { 315 try { 316 lock.await(timeoutMs, TimeUnit.MILLISECONDS); 317 } catch (InterruptedException e) { 318 // do nothing 319 } 320 } 321 } 322 323 /** 324 * Executes the given shell command and returns the output in a string. Note that even 325 * if we don't care about the output, we have to read the stream completely to make the 326 * command execute. 327 */ 328 public static String executeShellCommand(Instrumentation instrumentation, 329 String command) throws Exception { 330 final ParcelFileDescriptor pfd = 331 instrumentation.getUiAutomation().executeShellCommand(command); 332 BufferedReader br = null; 333 try (InputStream in = new FileInputStream(pfd.getFileDescriptor())) { 334 br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); 335 String str = null; 336 StringBuilder out = new StringBuilder(); 337 while ((str = br.readLine()) != null) { 338 out.append(str); 339 } 340 return out.toString(); 341 } finally { 342 if (br != null) { 343 closeQuietly(br); 344 } 345 closeQuietly(pfd); 346 } 347 } 348 349 private static void closeQuietly(AutoCloseable closeable) { 350 if (closeable != null) { 351 try { 352 closeable.close(); 353 } catch (RuntimeException rethrown) { 354 throw rethrown; 355 } catch (Exception ignored) { 356 } 357 } 358 } 359 360 /** 361 * Waits for the {@link CountDownLatch} to count down to 0 and then returns without reseting 362 * the latch. 363 * @param lock the latch that the system will wait on. 364 * @return true if the latch was released successfully, false if the latch timed out before 365 * resetting. 366 */ 367 public static boolean waitForLatchCountDown(CountDownLatch lock) { 368 if (lock == null) { 369 return false; 370 } 371 372 boolean success; 373 try { 374 success = lock.await(5000, TimeUnit.MILLISECONDS); 375 } catch (InterruptedException ie) { 376 return false; 377 } 378 379 return success; 380 } 381 382 /** 383 * Waits for the {@link CountDownLatch} to count down to 0 and then returns a new reset latch. 384 * @param lock The lock that will await a countDown to 0. 385 * @return a new reset {@link CountDownLatch} if the lock successfully counted down to 0 or 386 * null if the operation timed out. 387 */ 388 public static CountDownLatch waitForLock(CountDownLatch lock) { 389 boolean success = waitForLatchCountDown(lock); 390 if (success) { 391 return new CountDownLatch(1); 392 } else { 393 return null; 394 } 395 } 396 397 /** 398 * Adds a new incoming call. 399 * 400 * @param instrumentation the Instrumentation, used for shell command execution. 401 * @param telecomManager the TelecomManager. 402 * @param handle the PhoneAccountHandle associated with the call. 403 * @param address the incoming address. 404 * @return the new self-managed incoming call. 405 */ 406 public static void addIncomingCall(Instrumentation instrumentation, 407 TelecomManager telecomManager, PhoneAccountHandle handle, 408 Uri address) { 409 410 // Inform telecom of new incoming self-managed connection. 411 Bundle extras = new Bundle(); 412 extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, address); 413 telecomManager.addNewIncomingCall(handle, extras); 414 415 // Wait for Telecom to finish creating the new connection. 416 try { 417 waitOnAllHandlers(instrumentation); 418 } catch (Exception e) { 419 TestCase.fail("Failed to wait on handlers"); 420 } 421 } 422 public static boolean hasBluetoothFeature() { 423 return InstrumentationRegistry.getContext().getPackageManager(). 424 hasSystemFeature(PackageManager.FEATURE_BLUETOOTH); 425 } 426 public static BluetoothDevice makeBluetoothDevice(String address) { 427 if (!HAS_BLUETOOTH) return null; 428 Parcel p1 = Parcel.obtain(); 429 p1.writeString(address); 430 p1.setDataPosition(0); 431 BluetoothDevice device = BluetoothDevice.CREATOR.createFromParcel(p1); 432 p1.recycle(); 433 return device; 434 } 435 436 /** 437 * Places a new outgoing call. 438 * 439 * @param telecomManager the TelecomManager. 440 * @param handle the PhoneAccountHandle associated with the call. 441 * @param address outgoing call address. 442 * @return the new self-managed outgoing call. 443 */ 444 public static void placeOutgoingCall(Instrumentation instrumentation, 445 TelecomManager telecomManager, PhoneAccountHandle handle, 446 Uri address) { 447 // Inform telecom of new incoming self-managed connection. 448 Bundle extras = new Bundle(); 449 extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, handle); 450 telecomManager.placeCall(address, extras); 451 452 // Wait for Telecom to finish creating the new connection. 453 try { 454 waitOnAllHandlers(instrumentation); 455 } catch (Exception e) { 456 TestCase.fail("Failed to wait on handlers"); 457 } 458 } 459 460 /** 461 * Waits for a new SelfManagedConnection with the given address to be added. 462 * @param address The expected address. 463 * @return The SelfManagedConnection found. 464 */ 465 public static SelfManagedConnection waitForAndGetConnection(Uri address) { 466 // Wait for creation of the new connection. 467 if (!CtsSelfManagedConnectionService.waitForBinding()) { 468 TestCase.fail("Could not bind to Self-Managed ConnectionService"); 469 } 470 CtsSelfManagedConnectionService connectionService = 471 CtsSelfManagedConnectionService.getConnectionService(); 472 TestCase.assertTrue(connectionService.waitForUpdate( 473 CtsSelfManagedConnectionService.CONNECTION_CREATED_LOCK)); 474 475 Optional<SelfManagedConnection> connectionOptional = connectionService.getConnections() 476 .stream() 477 .filter(connection -> address.equals(connection.getAddress())) 478 .findFirst(); 479 assert(connectionOptional.isPresent()); 480 return connectionOptional.get(); 481 } 482 483 /** 484 * Utility class used to track the number of times a callback was invoked, and the arguments it 485 * was invoked with. This class is prefixed Invoke rather than the more typical Call for 486 * disambiguation purposes. 487 */ 488 public static final class InvokeCounter { 489 private final String mName; 490 private final Object mLock = new Object(); 491 private final ArrayList<Object[]> mInvokeArgs = new ArrayList<>(); 492 493 private int mInvokeCount; 494 495 public InvokeCounter(String callbackName) { 496 mName = callbackName; 497 } 498 499 public void invoke(Object... args) { 500 synchronized (mLock) { 501 mInvokeCount++; 502 mInvokeArgs.add(args); 503 mLock.notifyAll(); 504 } 505 } 506 507 public Object[] getArgs(int index) { 508 synchronized (mLock) { 509 return mInvokeArgs.get(index); 510 } 511 } 512 513 public int getInvokeCount() { 514 synchronized (mLock) { 515 return mInvokeCount; 516 } 517 } 518 519 public void waitForCount(int count) { 520 waitForCount(count, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS); 521 } 522 523 public void waitForCount(int count, long timeoutMillis) { 524 waitForCount(count, timeoutMillis, null); 525 } 526 527 public void waitForCount(long timeoutMillis) { 528 synchronized (mLock) { 529 try { 530 mLock.wait(timeoutMillis); 531 }catch (InterruptedException ex) { 532 ex.printStackTrace(); 533 } 534 } 535 } 536 537 public void waitForCount(int count, long timeoutMillis, String message) { 538 synchronized (mLock) { 539 final long startTimeMillis = SystemClock.uptimeMillis(); 540 while (mInvokeCount < count) { 541 try { 542 final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; 543 final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis; 544 if (remainingTimeMillis <= 0) { 545 if (message != null) { 546 TestCase.fail(message); 547 } else { 548 TestCase.fail(String.format("Expected %s to be called %d times.", 549 mName, count)); 550 } 551 } 552 mLock.wait(timeoutMillis); 553 } catch (InterruptedException ie) { 554 /* ignore */ 555 } 556 } 557 } 558 } 559 560 /** 561 * Waits for a predicate to return {@code true} within the specified timeout. Uses the 562 * {@link #mLock} for this {@link InvokeCounter} to eliminate the need to perform busy-wait. 563 * @param predicate The predicate. 564 * @param timeoutMillis The timeout. 565 */ 566 public void waitForPredicate(Predicate predicate, long timeoutMillis) { 567 synchronized (mLock) { 568 long startTimeMillis = SystemClock.uptimeMillis(); 569 long elapsedTimeMillis = 0; 570 long remainingTimeMillis = timeoutMillis; 571 Object foundValue = null; 572 boolean wasFound = false; 573 do { 574 try { 575 mLock.wait(timeoutMillis); 576 foundValue = (mInvokeArgs.get(mInvokeArgs.size()-1))[0]; 577 wasFound = predicate.test(foundValue); 578 elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; 579 remainingTimeMillis = timeoutMillis - elapsedTimeMillis; 580 } catch (InterruptedException ie) { 581 /* ignore */ 582 } 583 } while (!wasFound && remainingTimeMillis > 0); 584 if (wasFound) { 585 return; 586 } else if (remainingTimeMillis <= 0) { 587 TestCase.fail("Expected value not found within time limit"); 588 } 589 } 590 } 591 592 public void clearArgs() { 593 synchronized (mLock) { 594 mInvokeArgs.clear(); 595 } 596 } 597 } 598 599 private static long getCurrentUserSerialNumber(Instrumentation instrumentation) { 600 UserManager userManager = 601 instrumentation.getContext().getSystemService(UserManager.class); 602 return userManager.getSerialNumberForUser(Process.myUserHandle()); 603 } 604 } 605