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