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