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