1 /* 2 * Copyright (C) 2018 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 com.android.cts.appbinding; 17 18 import com.android.tradefed.util.RunUtil; 19 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertNull; 22 import static org.junit.Assert.fail; 23 24 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 25 import com.android.tradefed.build.IBuildInfo; 26 import com.android.tradefed.device.DeviceNotAvailableException; 27 import com.android.tradefed.log.LogUtil.CLog; 28 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 29 import com.android.tradefed.testtype.IBuildReceiver; 30 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 31 import com.android.tradefed.testtype.junit4.DeviceTestRunOptions; 32 33 import org.junit.After; 34 import org.junit.Before; 35 import org.junit.Test; 36 import org.junit.runner.RunWith; 37 38 import java.util.regex.Matcher; 39 import java.util.regex.Pattern; 40 41 @RunWith(DeviceJUnit4ClassRunner.class) 42 public class AppBindingHostTest extends BaseHostJUnit4Test implements IBuildReceiver { 43 44 private static final boolean SKIP_UNINSTALL = false; 45 46 private static final String APK_1 = "CtsAppBindingService1.apk"; 47 private static final String APK_2 = "CtsAppBindingService2.apk"; 48 private static final String APK_3 = "CtsAppBindingService3.apk"; 49 private static final String APK_4 = "CtsAppBindingService4.apk"; 50 private static final String APK_5 = "CtsAppBindingService5.apk"; 51 private static final String APK_6 = "CtsAppBindingService6.apk"; 52 private static final String APK_7 = "CtsAppBindingService7.apk"; 53 private static final String APK_B = "CtsAppBindingServiceB.apk"; 54 55 private static final String PACKAGE_A = "com.android.cts.appbinding.app"; 56 private static final String PACKAGE_B = "com.android.cts.appbinding.app.b"; 57 58 private static final String PACKAGE_A_PROC = PACKAGE_A + ":persistent"; 59 60 private static final String APP_BINDING_SETTING = "app_binding_constants"; 61 62 private static final String SERVICE_1 = "com.android.cts.appbinding.app.MyService"; 63 private static final String SERVICE_2 = "com.android.cts.appbinding.app.MyService2"; 64 65 private IBuildInfo mCtsBuild; 66 private int mCurrentUserId; 67 68 private static final int DEFAULT_TIMEOUT_SEC = 30; 69 70 private interface ThrowingRunnable { run()71 void run() throws Throwable; 72 } 73 74 @Override setBuild(IBuildInfo buildInfo)75 public void setBuild(IBuildInfo buildInfo) { 76 mCtsBuild = buildInfo; 77 } 78 installAppAsUser(String appFileName, boolean grantPermissions, int userId)79 private void installAppAsUser(String appFileName, boolean grantPermissions, int userId) 80 throws Exception { 81 CLog.d("Installing app " + appFileName + " for user " + userId); 82 CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild); 83 String result = getDevice().installPackageForUser( 84 buildHelper.getTestFile(appFileName), true, grantPermissions, userId, "-t"); 85 assertNull("Failed to install " + appFileName + " for user " + userId + ": " + result, 86 result); 87 88 waitForBroadcastIdle(); 89 } 90 waitForBroadcastIdle()91 private void waitForBroadcastIdle() throws Exception { 92 runCommand("am wait-for-broadcast-idle"); 93 RunUtil.getDefault().sleep(100); // Just wait a bit to make sure the system isn't too busy... 94 } 95 runCommand(String command)96 private String runCommand(String command) throws Exception { 97 return runCommand(command, "", true); 98 } 99 runCommand(String command, String expectedOutputPattern)100 private String runCommand(String command, String expectedOutputPattern) throws Exception { 101 return runCommand(command, expectedOutputPattern, true); 102 } 103 runCommandAndNotMatch(String command, String expectedOutputPattern)104 private String runCommandAndNotMatch(String command, String expectedOutputPattern) 105 throws Exception { 106 return runCommand(command, expectedOutputPattern, false); 107 } 108 runCommand(String command, String expectedOutputPattern, boolean shouldMatch)109 private String runCommand(String command, String expectedOutputPattern, 110 boolean shouldMatch) throws Exception { 111 CLog.d("Executing command: " + command); 112 final String output = getDevice().executeShellCommand(command); 113 114 CLog.d("Output:\n" 115 + "====================\n" 116 + output 117 + "===================="); 118 119 final Pattern pat = Pattern.compile( 120 expectedOutputPattern, Pattern.MULTILINE | Pattern.COMMENTS); 121 if (pat.matcher(output.trim()).find() != shouldMatch) { 122 fail("Output from \"" + command + "\" " 123 + (shouldMatch ? "didn't match" : "unexpectedly matched") 124 + " \"" + expectedOutputPattern + "\""); 125 } 126 return output; 127 } 128 runCommandAndExtract(String command, String startPattern, boolean startInclusive, String endPattern, boolean endInclusive)129 private String runCommandAndExtract(String command, 130 String startPattern, boolean startInclusive, 131 String endPattern, boolean endInclusive) throws Exception { 132 final String[] output = runCommand(command).split("\\n"); 133 final StringBuilder sb = new StringBuilder(); 134 135 final Pattern start = Pattern.compile(startPattern, Pattern.COMMENTS); 136 final Pattern end = Pattern.compile(endPattern, Pattern.COMMENTS); 137 138 boolean in = false; 139 for (String s : output) { 140 if (in) { 141 if (end.matcher(s.trim()).find()) { 142 if (endInclusive) { 143 sb.append(s); 144 sb.append("\n"); 145 } 146 break; 147 } 148 sb.append(s); 149 sb.append("\n"); 150 } else { 151 if (start.matcher(s.trim()).find()) { 152 if (startInclusive) { 153 sb.append(s); 154 sb.append("\n"); 155 } 156 continue; 157 } 158 in = true; 159 } 160 } 161 162 return sb.toString(); 163 } 164 updateConstants(String settings)165 private void updateConstants(String settings) throws Exception { 166 runCommand("settings put global " + APP_BINDING_SETTING + " '" + settings + "'"); 167 } 168 isSmsCapable()169 private boolean isSmsCapable() throws Exception { 170 String output = runCommand("dumpsys phone"); 171 if (output.contains("isSmsCapable=true")) { 172 CLog.d("Device is SMS capable"); 173 return true; 174 } 175 CLog.d("Device is not SMS capable"); 176 return false; 177 } 178 setSmsApp(String pkg, int userId)179 private void setSmsApp(String pkg, int userId) throws Throwable { 180 runWithRetries(300, () -> { 181 String output1 = runCommand("cmd role get-role-holders --user " + userId 182 + " android.app.role.SMS "); 183 if (output1.equals(pkg)) { 184 CLog.d(pkg + " has been set default sms app."); 185 } else { 186 String output2 = runCommand("cmd role add-role-holder --user " + userId 187 + " android.app.role.SMS " + pkg); 188 if (output2.contains("TimeoutException")) { 189 RunUtil.getDefault().sleep(10000); 190 throw new RuntimeException("cmd role add-role-holder timeout."); 191 } 192 } 193 }); 194 } 195 uninstallTestApps(boolean always)196 private void uninstallTestApps(boolean always) throws Exception { 197 if (SKIP_UNINSTALL && !always) { 198 return; 199 } 200 getDevice().uninstallPackage(PACKAGE_A); 201 getDevice().uninstallPackage(PACKAGE_B); 202 203 waitForBroadcastIdle(); 204 } 205 runWithRetries(int timeoutSeconds, ThrowingRunnable r)206 private void runWithRetries(int timeoutSeconds, ThrowingRunnable r) throws Throwable { 207 final long timeout = System.currentTimeMillis() + timeoutSeconds * 1000; 208 Throwable lastThrowable = null; 209 210 int sleep = 200; 211 while (System.currentTimeMillis() < timeout) { 212 try { 213 r.run(); 214 return; 215 } catch (Throwable th) { 216 lastThrowable = th; 217 } 218 RunUtil.getDefault().sleep(sleep); 219 sleep = Math.min(1000, sleep * 2); 220 } 221 throw lastThrowable; 222 } 223 224 @Before setUp()225 public void setUp() throws Exception { 226 // Reset to the default setting. 227 updateConstants(","); 228 229 uninstallTestApps(true); 230 231 mCurrentUserId = getDevice().getCurrentUser(); 232 } 233 234 @After tearDown()235 public void tearDown() throws Exception { 236 uninstallTestApps(false); 237 238 // Reset to the default setting. 239 updateConstants(","); 240 } 241 installAndCheckBound(String apk, String packageName, String serviceClass, int userId)242 private void installAndCheckBound(String apk, String packageName, 243 String serviceClass, int userId) throws Throwable { 244 // Install 245 installAppAsUser(apk, true, userId); 246 247 // Set as the default app 248 setSmsApp(packageName, userId); 249 250 checkBound(packageName, serviceClass, userId); 251 } 252 checkBound(String packageName, String serviceClass, int userId)253 private void checkBound(String packageName, String serviceClass, int userId) throws Throwable { 254 runWithRetries(DEFAULT_TIMEOUT_SEC, () -> { 255 runCommand("dumpsys activity service " + packageName + "/" + serviceClass, 256 Pattern.quote("[" + packageName + "]") + " .* " 257 + Pattern.quote("[" + serviceClass + "]")); 258 }); 259 260 // This should contain: 261 // "conn,0,[Default SMS app],PACKAGE,CLASS,bound,connected" 262 263 // The binding information is propagated asynchronously, so we need a retry here too. 264 // (Even though the activity manager said it's already bound.) 265 runWithRetries(DEFAULT_TIMEOUT_SEC, () -> { 266 runCommand("dumpsys app_binding -s", 267 "^" + Pattern.quote("conn,[Default SMS app]," + userId + "," + packageName + "," 268 + serviceClass + ",bound,connected,")); 269 }); 270 } 271 installAndCheckNotBound(String apk, String packageName, int userId, String expectedErrorPattern)272 private void installAndCheckNotBound(String apk, String packageName, int userId, 273 String expectedErrorPattern) throws Throwable { 274 // Install 275 installAppAsUser(apk, true, userId); 276 277 // Set as the default app 278 setSmsApp(packageName, userId); 279 280 checkNotBoundWithError(packageName, userId, expectedErrorPattern); 281 } 282 checkNotBoundWithError(String packageName, int userId, String expectedErrorPattern)283 private void checkNotBoundWithError(String packageName, int userId, 284 String expectedErrorPattern) throws Throwable { 285 // This should contain: 286 // "finder,0,[Default SMS app],PACKAGE,null,ERROR-MESSAGE" 287 runWithRetries(DEFAULT_TIMEOUT_SEC, () -> { 288 runCommand("dumpsys app_binding -s", 289 "^" + Pattern.quote("finder,[Default SMS app]," + userId + "," 290 + packageName + ",null,") + ".*" 291 + Pattern.quote(expectedErrorPattern) + ".*$"); 292 }); 293 } 294 checkPackageNotBound(String packageName, int userId)295 private void checkPackageNotBound(String packageName, int userId) throws Throwable { 296 // This should contain: 297 // "finder,0,[Default SMS app],DIFFERENT-PACKAGE,..." 298 runWithRetries(DEFAULT_TIMEOUT_SEC, () -> { 299 runCommand("dumpsys app_binding -s", 300 "^" + Pattern.quote("finder,[Default SMS app]," + userId + ",") 301 + "(?!" // Negative look ahead 302 + Pattern.quote(packageName + ",") 303 + ")"); 304 }); 305 } 306 assertOomAdjustment(String packageName, String processName, int oomAdj)307 private void assertOomAdjustment(String packageName, String processName, int oomAdj) 308 throws Exception { 309 final String output = runCommandAndExtract("dumpsys activity -a p " + packageName, 310 "\\sProcessRecord\\{.*\\:" + Pattern.quote(processName) + "\\/", false, 311 "^\\s*oom:", true); 312 /* Example: 313 ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes) 314 All known processes: 315 *APP* UID 10196 ProcessRecord{ef7dd8f 29993:com.android.cts.appbinding.app:persistent/u0a196} 316 user #0 uid=10196 gids={50196, 20196, 9997} 317 mRequiredAbi=arm64-v8a instructionSet=null 318 dir=/data/app/com.android.cts.appbinding.app-zvJ1Z44jYKxm-K0HLBRtLA==/base.apk publicDir=/da... 319 packageList={com.android.cts.appbinding.app} 320 compat={560dpi} 321 thread=android.app.IApplicationThread$Stub$Proxy@a5181c 322 pid=29993 starting=false 323 lastActivityTime=-14s282ms lastPssTime=-14s316ms pssStatType=0 nextPssTime=+5s718ms 324 adjSeq=35457 lruSeq=0 lastPss=0.00 lastSwapPss=0.00 lastCachedPss=0.00 lastCachedSwapPss=0.00 325 procStateMemTracker: best=4 () / pending state=2 highest=2 1.0x 326 cached=false empty=true 327 oom: max=1001 curRaw=200 setRaw=200 cur=200 set=200 328 mCurSchedGroup=2 setSchedGroup=2 systemNoUi=false trimMemoryLevel=0 329 curProcState=4 mRepProcState=4 pssProcState=19 setProcState=4 lastStateTime=-14s282ms 330 reportedInteraction=true time=-14s284ms 331 startSeq=369 332 lastRequestedGc=-14s283ms lastLowMemory=-14s283ms reportLowMemory=false 333 Configuration={1.0 ?mcc?mnc [en_US] ldltr sw411dp w411dp h746dp 560dpi nrml long widecg ... 334 OverrideConfiguration={0.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ... 335 mLastReportedConfiguration={0.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ... 336 Services: 337 - ServiceRecord{383eb86 u0 com.android.cts.appbinding.app/.MyService} 338 Connected Providers: 339 - 54bfc25/com.android.providers.settings/.SettingsProvider->29993:com.android.cts.... 340 341 Process LRU list (sorted by oom_adj, 50 total, non-act at 4, non-svc at 4): 342 Proc #10: prcp F/ /BFGS trm: 0 29993:com.android.cts.appbinding.app:persistent/u0a196 (service) 343 com.android.cts.appbinding.app/.MyService<=Proc{1332:system/1000} 344 */ 345 final Pattern pat = Pattern.compile("\\soom:\\s.* set=(\\d+)$", Pattern.MULTILINE); 346 final Matcher m = pat.matcher(output); 347 if (!m.find()) { 348 fail("Unable to fild the oom: line for process " + processName); 349 } 350 final String oom = m.group(1); 351 assertEquals("Unexpected oom adjustment:", String.valueOf(oomAdj), oom); 352 } 353 354 /** 355 * Install APK 1 and make it the default SMS app and make sure the service gets bound. 356 */ 357 @Test testSimpleBind1()358 public void testSimpleBind1() throws Throwable { 359 if (!isSmsCapable()) { 360 // device not supporting sms. cannot run the test. 361 return; 362 } 363 364 installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, mCurrentUserId); 365 } 366 367 /** 368 * Install APK 2 and make it the default SMS app and make sure the service gets bound. 369 */ 370 @Test testSimpleBind2()371 public void testSimpleBind2() throws Throwable { 372 if (!isSmsCapable()) { 373 // device not supporting sms. cannot run the test. 374 return; 375 } 376 377 installAndCheckBound(APK_2, PACKAGE_A, SERVICE_2, mCurrentUserId); 378 } 379 380 /** 381 * Install APK B and make it the default SMS app and make sure the service gets bound. 382 */ 383 @Test testSimpleBindB()384 public void testSimpleBindB() throws Throwable { 385 if (!isSmsCapable()) { 386 // device not supporting sms. cannot run the test. 387 return; 388 } 389 390 installAndCheckBound(APK_B, PACKAGE_B, SERVICE_1, mCurrentUserId); 391 } 392 393 /** 394 * APK 3 doesn't have a valid service to be bound. 395 */ 396 @Test testSimpleNotBound3()397 public void testSimpleNotBound3() throws Throwable { 398 if (!isSmsCapable()) { 399 // device not supporting sms. cannot run the test. 400 return; 401 } 402 403 installAndCheckNotBound(APK_3, PACKAGE_A, mCurrentUserId, 404 "must be protected with android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE"); 405 } 406 407 /** 408 * APK 4 doesn't have a valid service to be bound. 409 */ 410 @Test testSimpleNotBound4()411 public void testSimpleNotBound4() throws Throwable { 412 if (!isSmsCapable()) { 413 // device not supporting sms. cannot run the test. 414 return; 415 } 416 417 installAndCheckNotBound(APK_4, PACKAGE_A, mCurrentUserId, "More than one"); 418 } 419 420 /** 421 * APK 5 doesn't have a valid service to be bound. 422 */ 423 @Test testSimpleNotBound5()424 public void testSimpleNotBound5() throws Throwable { 425 if (!isSmsCapable()) { 426 // device not supporting sms. cannot run the test. 427 return; 428 } 429 430 installAndCheckNotBound(APK_5, PACKAGE_A, mCurrentUserId, 431 "Service with android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE not found"); 432 } 433 434 /** 435 * APK 6's service doesn't have android:process. 436 */ 437 @Test testSimpleNotBound6()438 public void testSimpleNotBound6() throws Throwable { 439 if (!isSmsCapable()) { 440 // device not supporting sms. cannot run the test. 441 return; 442 } 443 444 installAndCheckNotBound(APK_6, PACKAGE_A, mCurrentUserId, 445 "Service must not run on the main process"); 446 } 447 448 /** 449 * Make sure when the SMS app gets updated, the service still gets bound correctly. 450 */ 451 @Test testUpgrade()452 public void testUpgrade() throws Throwable { 453 if (!isSmsCapable()) { 454 // device not supporting sms. cannot run the test. 455 return; 456 } 457 458 // Replace existing package without uninstalling. 459 installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, mCurrentUserId); 460 installAndCheckBound(APK_2, PACKAGE_A, SERVICE_2, mCurrentUserId); 461 installAndCheckNotBound(APK_3, PACKAGE_A, mCurrentUserId, 462 "must be protected with android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE"); 463 installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, mCurrentUserId); 464 installAndCheckNotBound(APK_4, PACKAGE_A, mCurrentUserId, "More than one"); 465 } 466 enableTargetService(boolean enable)467 private void enableTargetService(boolean enable) throws DeviceNotAvailableException { 468 runDeviceTests(new DeviceTestRunOptions(PACKAGE_A) 469 .setTestClassName("com.android.cts.appbinding.app.MyEnabler") 470 .setTestMethodName(enable ? "enableService" : "disableService") 471 .setUserId(mCurrentUserId)); 472 } 473 474 /** 475 * Make sure the service responds to setComponentEnabled. 476 */ 477 @Test testServiceEnabledByDefault()478 public void testServiceEnabledByDefault() throws Throwable { 479 if (!isSmsCapable()) { 480 // device not supporting sms. cannot run the test. 481 return; 482 } 483 484 installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, mCurrentUserId); 485 486 // Disable the component and now it should be unbound. 487 488 enableTargetService(false); 489 490 RunUtil.getDefault().sleep(2); // Technically not needed, but allow the system to handle the broadcast. 491 492 checkNotBoundWithError(PACKAGE_A, mCurrentUserId, 493 "Service with android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE not found"); 494 495 // Enable the component and now it should be bound. 496 enableTargetService(true); 497 498 RunUtil.getDefault().sleep(2); // Technically not needed, but allow the system to handle the broadcast. 499 500 checkBound(PACKAGE_A, SERVICE_1, mCurrentUserId); 501 } 502 503 /** 504 * Make sure the service responds to setComponentEnabled. 505 */ 506 @Test testServiceDisabledByDefault()507 public void testServiceDisabledByDefault() throws Throwable { 508 if (!isSmsCapable()) { 509 // device not supporting sms. cannot run the test. 510 return; 511 } 512 513 // The service is disabled by default, so not bound. 514 installAndCheckNotBound(APK_7, PACKAGE_A, mCurrentUserId, 515 "Service with android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE not found"); 516 517 // Enable the component and now it should be bound. 518 enableTargetService(true); 519 520 RunUtil.getDefault().sleep(2); // Technically not needed, but allow the system to handle the broadcast. 521 522 checkBound(PACKAGE_A, SERVICE_1, mCurrentUserId); 523 524 // Disable the component and now it should be unbound. 525 526 enableTargetService(false); 527 528 RunUtil.getDefault().sleep(2); // Technically not needed, but allow the system to handle the broadcast. 529 530 checkNotBoundWithError(PACKAGE_A, mCurrentUserId, 531 "Service with android.telephony.action.CARRIER_MESSAGING_CLIENT_SERVICE not found"); 532 } 533 534 /** 535 * Make sure when the SMS app is uninstalled, the binding will be gone. 536 */ 537 @Test testUninstall()538 public void testUninstall() throws Throwable { 539 if (!isSmsCapable()) { 540 // device not supporting sms. cannot run the test. 541 return; 542 } 543 544 // Replace existing package without uninstalling. 545 installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, mCurrentUserId); 546 getDevice().uninstallPackage(PACKAGE_A); 547 checkPackageNotBound(PACKAGE_A, mCurrentUserId); 548 549 // Try with different APKs, just to make sure. 550 installAndCheckBound(APK_B, PACKAGE_B, SERVICE_1, mCurrentUserId); 551 getDevice().uninstallPackage(PACKAGE_B); 552 checkPackageNotBound(PACKAGE_B, mCurrentUserId); 553 554 installAndCheckBound(APK_2, PACKAGE_A, SERVICE_2, mCurrentUserId); 555 getDevice().uninstallPackage(PACKAGE_A); 556 checkPackageNotBound(PACKAGE_A, mCurrentUserId); 557 } 558 559 /** 560 * Make sure when the SMS app changes, the service still gets bound correctly. 561 */ 562 @Test testSwitchDefaultApp()563 public void testSwitchDefaultApp() throws Throwable { 564 if (!isSmsCapable()) { 565 // device not supporting sms. cannot run the test. 566 return; 567 } 568 569 installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, mCurrentUserId); 570 installAndCheckBound(APK_B, PACKAGE_B, SERVICE_1, mCurrentUserId); 571 installAndCheckBound(APK_2, PACKAGE_A, SERVICE_2, mCurrentUserId); 572 } 573 assertUserHasNoConnection(int userId)574 private void assertUserHasNoConnection(int userId) throws Throwable { 575 runWithRetries(DEFAULT_TIMEOUT_SEC, () -> { 576 runCommandAndNotMatch("dumpsys app_binding -s", 577 "^conn,\\[Default\\sSMS\\sapp\\]," + userId + ","); 578 }); 579 } 580 assertUserHasNoFinder(int userId)581 private void assertUserHasNoFinder(int userId) throws Throwable { 582 runWithRetries(DEFAULT_TIMEOUT_SEC, () -> { 583 runCommandAndNotMatch("dumpsys app_binding -s", 584 "^finder,\\[Default\\sSMS\\sapp\\]," + userId + ","); 585 }); 586 } 587 588 @Test testSecondaryUser()589 public void testSecondaryUser() throws Throwable { 590 if (!isSmsCapable()) { 591 // device not supporting sms. cannot run the test. 592 return; 593 } 594 595 if (!getDevice().isMultiUserSupported()) { 596 // device do not support multi-user. 597 return; 598 } 599 600 installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, mCurrentUserId); 601 602 final int userId = getDevice().createUser("test-user"); 603 try { 604 getDevice().startUser(userId); 605 606 // Install SMS app on the secondary user. 607 installAndCheckBound(APK_B, PACKAGE_B, SERVICE_1, userId); 608 609 // Package A should still be bound on user-0. 610 checkBound(PACKAGE_A, SERVICE_1, mCurrentUserId); 611 612 // Replace the app on the primary user with an invalid one. 613 installAndCheckNotBound(APK_3, PACKAGE_A, mCurrentUserId, 614 "must be protected with android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE"); 615 616 // Secondary user should still have a valid connection. 617 checkBound(PACKAGE_B, SERVICE_1, userId); 618 619 // Upgrade test: Try with apk 1, and then upgrade to apk 2. 620 installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, userId); 621 installAndCheckBound(APK_2, PACKAGE_A, SERVICE_2, userId); 622 623 // Stop the secondary user, now the binding should be gone. 624 getDevice().stopUser(userId); 625 626 // Now the connection should be removed. 627 assertUserHasNoConnection(userId); 628 629 // Start the secondary user again. 630 getDevice().startUser(userId); 631 632 // Now the binding should recover. 633 runWithRetries(DEFAULT_TIMEOUT_SEC, () -> { 634 checkBound(PACKAGE_A, SERVICE_2, userId); 635 }); 636 637 } finally { 638 getDevice().removeUser(userId); 639 } 640 assertUserHasNoConnection(userId); 641 assertUserHasNoFinder(userId); 642 } 643 644 @Test testCrashAndAutoRebind()645 public void testCrashAndAutoRebind() throws Throwable { 646 if (!isSmsCapable()) { 647 // device not supporting sms. cannot run the test. 648 return; 649 } 650 651 updateConstants( 652 "service_reconnect_backoff_sec=5" 653 + ",service_reconnect_backoff_increase=2" 654 + ",service_reconnect_max_backoff_sec=1000" 655 + ",service_stable_connection_threshold_sec=10"); 656 657 installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, mCurrentUserId); 658 659 // Ensure the expected status. 660 runWithRetries(DEFAULT_TIMEOUT_SEC, () -> { 661 runCommand("dumpsys app_binding -s", 662 "^conn,\\[Default\\sSMS\\sapp\\]," + mCurrentUserId + ",.*,bound,connected" 663 + ",\\#con=1,\\#dis=0,\\#died=0,backoff=5000"); 664 }); 665 666 // Let the service crash. 667 runCommand("dumpsys activity service " + PACKAGE_A + "/" + SERVICE_1 + " crash"); 668 669 // Now the connection disconnected and re-connected, so the counters increase. 670 // In this case, because binder-died isn't called, so backoff won't increase. 671 runWithRetries(DEFAULT_TIMEOUT_SEC, () -> { 672 runCommand("dumpsys app_binding -s", 673 "^conn,\\[Default\\sSMS\\sapp\\]," + mCurrentUserId + ",.*,bound,connected" 674 + ",\\#con=2,\\#dis=1,\\#died=0,backoff=5000"); 675 }); 676 677 // Force-stop the app. 678 runCommand("am force-stop " + PACKAGE_A); 679 680 // Force-stop causes a disconnect and a binder-died. Then it doubles the backoff. 681 runWithRetries(DEFAULT_TIMEOUT_SEC, () -> { 682 runCommand("dumpsys app_binding -s", 683 "^conn,\\[Default\\sSMS\\sapp\\]," + mCurrentUserId + ",.*,not-bound,not-connected" 684 + ",\\#con=2,\\#dis=2,\\#died=1,backoff=10000"); 685 }); 686 687 RunUtil.getDefault().sleep(5000); 688 689 // It should re-bind. 690 runWithRetries(10, () -> { 691 runCommand("dumpsys app_binding -s", 692 "^conn,\\[Default\\sSMS\\sapp\\]," + mCurrentUserId + ",.*,bound,connected" 693 + ",\\#con=3,\\#dis=2,\\#died=1,backoff=10000"); 694 }); 695 696 // Force-stop again. 697 runCommand("am force-stop " + PACKAGE_A); 698 699 runWithRetries(10, () -> { 700 runCommand("dumpsys app_binding -s", 701 "^conn,\\[Default\\sSMS\\sapp\\]," + mCurrentUserId + ",.*,not-bound,not-connected" 702 + ",\\#con=3,\\#dis=3,\\#died=2,backoff=20000"); 703 }); 704 705 RunUtil.getDefault().sleep(10000); 706 707 runWithRetries(10, () -> { 708 runCommand("dumpsys app_binding -s", 709 "^conn,\\[Default\\sSMS\\sapp\\]," + mCurrentUserId + ",.*,bound,connected" 710 + ",\\#con=4,\\#dis=3,\\#died=2,backoff=20000"); 711 }); 712 713 // If the connection lasts more than service_stable_connection_threshold_sec seconds, 714 // the backoff resets. 715 RunUtil.getDefault().sleep(10000); 716 717 runWithRetries(10, () -> { 718 runCommand("dumpsys app_binding -s", 719 "^conn,\\[Default\\sSMS\\sapp\\]," + mCurrentUserId + ",.*,bound,connected" 720 + ",\\#con=4,\\#dis=3,\\#died=2,backoff=5000"); 721 }); 722 } 723 724 /** 725 * Test the feature flag. 726 */ 727 @Test testFeatureDisabled()728 public void testFeatureDisabled() throws Throwable { 729 if (!isSmsCapable()) { 730 // device not supporting sms. cannot run the test. 731 return; 732 } 733 734 installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, mCurrentUserId); 735 736 updateConstants("sms_service_enabled=false"); 737 738 runWithRetries(DEFAULT_TIMEOUT_SEC, () -> { 739 checkNotBoundWithError("null", mCurrentUserId, "feature disabled"); 740 }); 741 742 updateConstants("sms_service_enabled=true"); 743 744 runWithRetries(DEFAULT_TIMEOUT_SEC, () -> { 745 checkBound(PACKAGE_A, SERVICE_1, mCurrentUserId); 746 }); 747 } 748 749 @Test testOomAdjustment()750 public void testOomAdjustment() throws Throwable { 751 if (!isSmsCapable()) { 752 // device not supporting sms. cannot run the test. 753 return; 754 } 755 756 installAndCheckBound(APK_1, PACKAGE_A, SERVICE_1, mCurrentUserId); 757 assertOomAdjustment(PACKAGE_A, PACKAGE_A_PROC, 200); 758 } 759 } 760