1 /* 2 * Copyright (C) 2019 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 17 package com.android.tests.stagedinstall; 18 19 import static com.android.cts.install.lib.InstallUtils.assertStatusFailure; 20 import static com.android.cts.install.lib.InstallUtils.assertStatusSuccess; 21 import static com.android.cts.install.lib.InstallUtils.getPackageInstaller; 22 import static com.android.cts.install.lib.PackageInstallerSessionInfoSubject.assertThat; 23 import static com.android.cts.shim.lib.ShimPackage.DIFFERENT_APEX_PACKAGE_NAME; 24 import static com.android.cts.shim.lib.ShimPackage.NOT_PRE_INSTALL_APEX_PACKAGE_NAME; 25 import static com.android.cts.shim.lib.ShimPackage.SHIM_APEX_PACKAGE_NAME; 26 import static com.android.cts.shim.lib.ShimPackage.SHIM_PACKAGE_NAME; 27 28 import static com.google.common.truth.Truth.assertThat; 29 import static com.google.common.truth.Truth.assertWithMessage; 30 31 import static org.junit.Assert.fail; 32 33 import android.Manifest; 34 import android.content.BroadcastReceiver; 35 import android.content.Context; 36 import android.content.Intent; 37 import android.content.IntentFilter; 38 import android.content.pm.ApplicationInfo; 39 import android.content.pm.PackageInfo; 40 import android.content.pm.PackageInstaller; 41 import android.content.pm.PackageManager; 42 import android.os.Handler; 43 import android.os.HandlerThread; 44 import android.util.Log; 45 46 import androidx.test.platform.app.InstrumentationRegistry; 47 48 import com.android.compatibility.common.util.SystemUtil; 49 import com.android.cts.install.lib.Install; 50 import com.android.cts.install.lib.InstallUtils; 51 import com.android.cts.install.lib.LocalIntentSender; 52 import com.android.cts.install.lib.TestApp; 53 import com.android.cts.install.lib.Uninstall; 54 55 import org.junit.After; 56 import org.junit.Before; 57 import org.junit.Test; 58 import org.junit.runner.RunWith; 59 import org.junit.runners.JUnit4; 60 61 import java.io.BufferedReader; 62 import java.io.BufferedWriter; 63 import java.io.File; 64 import java.io.FileReader; 65 import java.io.FileWriter; 66 import java.io.IOException; 67 import java.io.InputStream; 68 import java.nio.file.FileVisitResult; 69 import java.nio.file.Files; 70 import java.nio.file.Path; 71 import java.nio.file.Paths; 72 import java.nio.file.SimpleFileVisitor; 73 import java.nio.file.StandardCopyOption; 74 import java.nio.file.attribute.BasicFileAttributes; 75 import java.time.Duration; 76 import java.util.ArrayList; 77 import java.util.Arrays; 78 import java.util.HashSet; 79 import java.util.List; 80 import java.util.Set; 81 import java.util.concurrent.TimeUnit; 82 import java.util.concurrent.atomic.AtomicInteger; 83 import java.util.function.Consumer; 84 import java.util.stream.Collectors; 85 86 /** 87 * This series of tests are meant to be driven by a host, since some of the interactions being 88 * tested require the device to be rebooted, and some assertions to be verified post-reboot. 89 * The convention used here (not enforced) is that the test methods in this file will be named 90 * the same way as the test methods in the "host" class (see e.g. 91 * {@code com.android.test.stagedinstall.host.StagedInstallTest}), with an optional suffix preceded 92 * by an underscore, in case of multiple phases. 93 * Example: 94 * - In {@code com.android.test.stagedinstall.host.StagedInstallTest}: 95 * 96 * @Test 97 * public void testInstallStagedApk() throws Exception { 98 * runPhase("testInstallStagedApk_Commit"); 99 * getDevice().reboot(); 100 * runPhase("testInstallStagedApk_VerifyPostReboot"); 101 * } 102 * - In this class: 103 * @Test public void testInstallStagedApk_Commit() throws Exception; 104 * @Test public void testInstallStagedApk_VerifyPostReboot() throws Exception; 105 */ 106 @RunWith(JUnit4.class) 107 public class StagedInstallTest { 108 109 private static final String TAG = "StagedInstallTest"; 110 111 private File mTestStateFile = new File( 112 InstrumentationRegistry.getInstrumentation().getContext().getFilesDir(), 113 "ctsstagedinstall_state"); 114 115 private static final Duration WAIT_FOR_SESSION_REMOVED_TTL = Duration.ofSeconds(10); 116 private static final Duration SLEEP_DURATION = Duration.ofMillis(200); 117 118 private static final TestApp TESTAPP_SAME_NAME_AS_APEX = new TestApp( 119 "TestAppSamePackageNameAsApex", SHIM_APEX_PACKAGE_NAME, 1, /*isApex*/ false, 120 "StagedInstallTestAppSamePackageNameAsApex.apk"); 121 private static final TestApp Apex2DifferentCertificate = new TestApp( 122 "Apex2DifferentCertificate", SHIM_APEX_PACKAGE_NAME, 2, /*isApex*/true, 123 "com.android.apex.cts.shim.v2_different_certificate.apex"); 124 private static final TestApp Apex2DifferentPackageName = new TestApp( 125 "Apex2DifferentPackageName", DIFFERENT_APEX_PACKAGE_NAME, 2, /*isApex*/true, 126 "com.android.apex.cts.shim.v2_different_package_name.apex"); 127 private static final TestApp Apex2SignedBob = new TestApp( 128 "Apex2SignedBob", SHIM_APEX_PACKAGE_NAME, 2, /*isApex*/true, 129 "com.android.apex.cts.shim.v2_signed_bob.apex"); 130 private static final TestApp Apex2SignedBobRot = new TestApp( 131 "Apex2SignedBobRot", SHIM_APEX_PACKAGE_NAME, 2, /*isApex*/true, 132 "com.android.apex.cts.shim.v2_signed_bob_rot.apex"); 133 private static final TestApp Apex2SignedBobRotRollback = new TestApp( 134 "Apex2SignedBobRotRollback", SHIM_APEX_PACKAGE_NAME, 2, /*isApex*/true, 135 "com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex"); 136 private static final TestApp ApexNoHashtree2 = new TestApp( 137 "Apex2", SHIM_APEX_PACKAGE_NAME, 2, /*isApex*/true, 138 "com.android.apex.cts.shim.v2_no_hashtree.apex"); 139 private static final TestApp ApexWrongSha2 = new TestApp( 140 "ApexWrongSha2", SHIM_APEX_PACKAGE_NAME, 2, /*isApex*/true, 141 "com.android.apex.cts.shim.v2_wrong_sha.apex"); 142 private static final TestApp Apex2WithoutApkInApex = new TestApp( 143 "Apex2WithoutApkInApex", SHIM_APEX_PACKAGE_NAME, 2, /*isApex*/true, 144 "com.android.apex.cts.shim.v2_without_apk_in_apex.apex"); 145 private static final TestApp Apex3SignedBob = new TestApp( 146 "Apex3SignedBob", SHIM_APEX_PACKAGE_NAME, 3, /*isApex*/true, 147 "com.android.apex.cts.shim.v3_signed_bob.apex"); 148 private static final TestApp Apex3SignedBobRot = new TestApp( 149 "Apex3SignedBobRot", SHIM_APEX_PACKAGE_NAME, 3, /*isApex*/true, 150 "com.android.apex.cts.shim.v3_signed_bob_rot.apex"); 151 private static final TestApp ApexNotPreInstalled = new TestApp( 152 "ApexNotPreInstalled", NOT_PRE_INSTALL_APEX_PACKAGE_NAME, 3, /*isApex*/true, 153 "com.android.apex.cts.shim_not_pre_installed.apex"); 154 private static final TestApp Apex2SdkTargetP = new TestApp( 155 "StagedInstallTestApexV2_SdkTargetP", SHIM_APEX_PACKAGE_NAME, 2, 156 /*isApex*/true, "com.android.apex.cts.shim.v2_sdk_target_p.apex"); 157 private static final TestApp Apex2ApkInApexSdkTargetP = new TestApp( 158 "StagedInstallTestApexV2_ApkInApexSdkTargetP", SHIM_APEX_PACKAGE_NAME, 2, 159 /*isApex*/true, "com.android.apex.cts.shim.v2_apk_in_apex_sdk_target_p.apex"); 160 private static final TestApp CorruptedApex_b146895998 = new TestApp( 161 "StagedInstallTestCorruptedApex_b146895998", "", 1, true, "corrupted_b146895998.apex"); 162 private static final TestApp Apex2NoApkSignature = new TestApp( 163 "StagedInstallTestApexV2_NoApkSignature", SHIM_APEX_PACKAGE_NAME, 1, 164 /*isApex*/true, "com.android.apex.cts.shim.v2_unsigned_apk_container.apex"); 165 private static final TestApp Apex2UnsignedPayload = new TestApp( 166 "StagedInstallTestApexV2_UnsignedPayload", SHIM_APEX_PACKAGE_NAME, 1, 167 /*isApex*/true, "com.android.apex.cts.shim.v2_unsigned_payload.apex"); 168 private static final TestApp Apex2SignPayloadWithDifferentKey = new TestApp( 169 "StagedInstallTestApexV2_SignPayloadWithDifferentKey", SHIM_APEX_PACKAGE_NAME, 1, 170 /*isApex*/true, "com.android.apex.cts.shim.v2_sign_payload_with_different_key.apex"); 171 private static final TestApp Apex2Rebootless = new TestApp( 172 "StagedInstallTestApexV2_Rebootless", SHIM_APEX_PACKAGE_NAME, 2, 173 /*isApex*/true, "com.android.apex.cts.shim.v2_rebootless.apex"); 174 private static final TestApp Apex3Rebootless = new TestApp( 175 "StagedInstallTestApexV3_Rebootless", SHIM_APEX_PACKAGE_NAME, 3, 176 /*isApex*/true, "com.android.apex.cts.shim.v3_rebootless.apex"); 177 private static final TestApp Apex2AsApk = new TestApp("Apex2", SHIM_APEX_PACKAGE_NAME, 2, 178 /*isApex*/false, "com.android.apex.cts.shim.v2.apex"); 179 180 @Before adoptShellPermissions()181 public void adoptShellPermissions() { 182 InstrumentationRegistry 183 .getInstrumentation() 184 .getUiAutomation() 185 .adoptShellPermissionIdentity( 186 Manifest.permission.PACKAGE_USAGE_STATS, 187 Manifest.permission.INSTALL_PACKAGES, 188 Manifest.permission.DELETE_PACKAGES); 189 } 190 191 @After dropShellPermissions()192 public void dropShellPermissions() { 193 InstrumentationRegistry 194 .getInstrumentation() 195 .getUiAutomation() 196 .dropShellPermissionIdentity(); 197 } 198 199 // This is marked as @Test to take advantage of @Before/@After methods of this class. Actual 200 // purpose of this method to be called before and after each test case of 201 // com.android.test.stagedinstall.host.StagedInstallTest to reduce tests flakiness. 202 @Test cleanUp()203 public void cleanUp() throws Exception { 204 PackageInstaller packageInstaller = getPackageInstaller(); 205 List<PackageInstaller.SessionInfo> stagedSessions = packageInstaller.getStagedSessions(); 206 for (PackageInstaller.SessionInfo sessionInfo : stagedSessions) { 207 if (sessionInfo.getParentSessionId() != PackageInstaller.SessionInfo.INVALID_ID 208 || sessionInfo.isStagedSessionApplied() 209 || sessionInfo.isStagedSessionFailed()) { 210 // Cannot abandon a child session; no need to abandon terminated sessions 211 continue; 212 } 213 try { 214 Log.i(TAG, "abandoning session " + sessionInfo.getSessionId()); 215 packageInstaller.abandonSession(sessionInfo.getSessionId()); 216 } catch (Exception e) { 217 Log.e(TAG, "Failed to abandon session " + sessionInfo.getSessionId(), e); 218 } 219 } 220 Uninstall.packages(TestApp.A, TestApp.B); 221 Files.deleteIfExists(mTestStateFile.toPath()); 222 } 223 224 @Test testFailInstallIfNoPermission()225 public void testFailInstallIfNoPermission() throws Exception { 226 dropShellPermissions(); 227 try { 228 createStagedSession(); 229 fail(); // Should have thrown SecurityException. 230 } catch (SecurityException e) { 231 // This would be a better version, but it requires a version of truth not present in the 232 // tree yet. 233 // assertThat(e).hasMessageThat().containsMatch(...); 234 assertThat(e.getMessage()).containsMatch( 235 "Neither user [0-9]+ nor current process has " 236 + "android.permission.INSTALL_PACKAGES"); 237 } 238 } 239 240 @Test testInstallStagedApk_Commit()241 public void testInstallStagedApk_Commit() throws Exception { 242 BroadcastCounter counter = new BroadcastCounter(PackageInstaller.ACTION_SESSION_COMMITTED); 243 int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 244 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 245 assertSessionReady(sessionId); 246 storeSessionId(sessionId); 247 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 248 counter.assertNoBroadcastReceived(); 249 } 250 251 @Test testInstallStagedApk_VerifyPostReboot()252 public void testInstallStagedApk_VerifyPostReboot() throws Exception { 253 BroadcastCounter counter = new BroadcastCounter(PackageInstaller.ACTION_SESSION_COMMITTED); 254 int sessionId = retrieveLastSessionId(); 255 assertSessionApplied(sessionId); 256 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1); 257 counter.assertNoBroadcastReceived(); 258 } 259 260 @Test testInstallStagedApk_AbandonSessionIsNoop()261 public void testInstallStagedApk_AbandonSessionIsNoop() throws Exception { 262 int sessionId = retrieveLastSessionId(); 263 assertSessionApplied(sessionId); 264 // Session is in a final state. Test that abandoning the session doesn't remove it from the 265 // session database. 266 abandonSession(sessionId); 267 assertSessionApplied(sessionId); 268 } 269 270 @Test testInstallMultipleStagedApks_Commit()271 public void testInstallMultipleStagedApks_Commit() throws Exception { 272 BroadcastCounter counter = new BroadcastCounter(PackageInstaller.ACTION_SESSION_COMMITTED); 273 int sessionId = stageMultipleApks(TestApp.A1, TestApp.B1) 274 .assertSuccessful().getSessionId(); 275 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 276 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1); 277 assertSessionReady(sessionId); 278 storeSessionId(sessionId); 279 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 280 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1); 281 counter.assertNoBroadcastReceived(); 282 } 283 284 @Test testInstallMultipleStagedApks_VerifyPostReboot()285 public void testInstallMultipleStagedApks_VerifyPostReboot() throws Exception { 286 BroadcastCounter counter = new BroadcastCounter(PackageInstaller.ACTION_SESSION_COMMITTED); 287 int sessionId = retrieveLastSessionId(); 288 assertSessionApplied(sessionId); 289 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1); 290 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1); 291 counter.assertNoBroadcastReceived(); 292 } 293 294 @Test testAbandonStagedApkBeforeReboot_CommitAndAbandon()295 public void testAbandonStagedApkBeforeReboot_CommitAndAbandon() throws Exception { 296 int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 297 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 298 PackageInstaller.SessionInfo session = InstallUtils.getStagedSessionInfo(sessionId); 299 assertSessionReady(sessionId); 300 abandonSession(sessionId); 301 InstallUtils.assertStagedSessionIsAbandoned(sessionId); 302 // Allow the session to be removed from PackageInstaller 303 Duration spentWaiting = Duration.ZERO; 304 while (spentWaiting.compareTo(WAIT_FOR_SESSION_REMOVED_TTL) < 0) { 305 session = getSessionInfo(sessionId); 306 if (session == null) { 307 Log.i(TAG, "Done waiting after " + spentWaiting); 308 break; 309 } 310 try { 311 Thread.sleep(SLEEP_DURATION.toMillis()); 312 spentWaiting = spentWaiting.plus(SLEEP_DURATION); 313 } catch (InterruptedException e) { 314 Thread.currentThread().interrupt(); 315 throw new RuntimeException(e); 316 } 317 } 318 assertThat(session).isNull(); 319 } 320 321 @Test testAbandonStagedApkBeforeReboot_VerifyPostReboot()322 public void testAbandonStagedApkBeforeReboot_VerifyPostReboot() throws Exception { 323 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 324 } 325 326 @Test testAbandonStagedApkBeforeReady_CommitAndAbandon()327 public void testAbandonStagedApkBeforeReady_CommitAndAbandon() throws Exception { 328 int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 329 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 330 abandonSession(sessionId); 331 InstallUtils.assertStagedSessionIsAbandoned(sessionId); 332 } 333 334 @Test testAbandonStagedApkBeforeReady_VerifyPostReboot()335 public void testAbandonStagedApkBeforeReady_VerifyPostReboot() throws Exception { 336 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 337 } 338 339 @Test testStageAnotherSessionImmediatelyAfterAbandon()340 public void testStageAnotherSessionImmediatelyAfterAbandon() throws Exception { 341 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 342 int sessionId = stageSingleApk(TestApp.Apex2).assertSuccessful().getSessionId(); 343 abandonSession(sessionId); 344 stageSingleApk(TestApp.Apex2).assertSuccessful(); 345 } 346 347 @Test testStageAnotherSessionImmediatelyAfterAbandonMultiPackage()348 public void testStageAnotherSessionImmediatelyAfterAbandonMultiPackage() throws Exception { 349 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 350 int sessionId = stageMultipleApks(TestApp.Apex2, TestApp.A1, TestApp.B1) 351 .assertSuccessful().getSessionId(); 352 abandonSession(sessionId); 353 stageSingleApk(TestApp.Apex2).assertSuccessful(); 354 } 355 356 @Test testNoSessionUpdatedBroadcastSentForStagedSessionAbandon()357 public void testNoSessionUpdatedBroadcastSentForStagedSessionAbandon() throws Exception { 358 BroadcastCounter counter = new BroadcastCounter(PackageInstaller.ACTION_SESSION_UPDATED); 359 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 360 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 361 // Using an apex in hopes that pre-reboot verification will take longer to complete 362 // and we will manage to abandon it before session becomes ready. 363 int sessionId = Install.multi(TestApp.A1, TestApp.Apex2).setStaged().createSession(); 364 LocalIntentSender sender = new LocalIntentSender(); 365 InstallUtils.openPackageInstallerSession(sessionId).commit(sender.getIntentSender()); 366 abandonSession(sessionId); 367 InstallUtils.assertStagedSessionIsAbandoned(sessionId); 368 counter.assertNoBroadcastReceived(); 369 } 370 371 @Test testGetActiveStagedSessions()372 public void testGetActiveStagedSessions() throws Exception { 373 PackageInstaller packageInstaller = getPackageInstaller(); 374 int firstSessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 375 int secondSessionId = stageSingleApk(TestApp.B1).assertSuccessful().getSessionId(); 376 List<Integer> stagedSessionIds = packageInstaller.getActiveStagedSessions() 377 .stream().map(s -> s.getSessionId()).collect(Collectors.toList()); 378 assertThat(stagedSessionIds).containsExactly(firstSessionId, secondSessionId); 379 380 // Verify no other session is considered as active staged session 381 List<PackageInstaller.SessionInfo> allSessions = packageInstaller.getAllSessions(); 382 for (PackageInstaller.SessionInfo session : allSessions) { 383 if (session.isStagedSessionActive()) { 384 assertThat(stagedSessionIds).contains(session.getSessionId()); 385 } 386 } 387 } 388 389 /** 390 * Verifies that active staged session fulfils conditions stated at 391 * {@link PackageInstaller.SessionInfo#isStagedSessionActive} 392 */ 393 @Test testIsStagedSessionActive()394 public void testIsStagedSessionActive() throws Exception { 395 PackageInstaller packageInstaller = getPackageInstaller(); 396 int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 397 398 List<PackageInstaller.SessionInfo> allSessions = packageInstaller.getAllSessions(); 399 boolean activeStagedSessionFound = false; 400 for (PackageInstaller.SessionInfo session : allSessions) { 401 // If it fulfils conditions, then it should be an active staged session 402 if (session.isStaged() && session.isCommitted() && !session.isStagedSessionApplied() 403 && !session.isStagedSessionFailed()) { 404 activeStagedSessionFound = true; 405 assertThat(session.getSessionId()).isEqualTo(sessionId); 406 assertThat(session.isStagedSessionActive()).isTrue(); 407 } else { 408 // Otherwise, it should not be marked as active staged session 409 assertThat(session.getSessionId()).isNotEqualTo(sessionId); 410 assertThat(session.isStagedSessionActive()).isFalse(); 411 } 412 } 413 assertWithMessage("Did not find any active staged session") 414 .that(activeStagedSessionFound).isTrue(); 415 } 416 417 @Test testGetActiveStagedSessionsNoSessionActive()418 public void testGetActiveStagedSessionsNoSessionActive() throws Exception { 419 PackageInstaller packageInstaller = getPackageInstaller(); 420 List<PackageInstaller.SessionInfo> sessions = packageInstaller.getActiveStagedSessions(); 421 assertThat(sessions).isEmpty(); 422 } 423 424 @Test testGetActiveStagedSessions_MultiApkSession()425 public void testGetActiveStagedSessions_MultiApkSession() throws Exception { 426 int firstSessionId = stageMultipleApks(TestApp.A1, TestApp.B1) 427 .assertSuccessful().getSessionId(); 428 int secondSessionId = stageMultipleApks(TestApp.C1) 429 .assertSuccessful().getSessionId(); 430 List<Integer> stagedSessionIds = getPackageInstaller().getActiveStagedSessions() 431 .stream().map(s -> s.getSessionId()).collect(Collectors.toList()); 432 assertThat(stagedSessionIds).containsExactly(firstSessionId, secondSessionId); 433 } 434 435 @Test testStagedInstallDowngrade_DowngradeNotRequested_Fails_Commit()436 public void testStagedInstallDowngrade_DowngradeNotRequested_Fails_Commit() throws Exception { 437 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 438 Install.single(TestApp.A2).commit(); 439 int sessionId = stageSingleApk(TestApp.A1).assertFailure().getSessionId(); 440 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2); 441 PackageInstaller.SessionInfo sessionInfo = getSessionInfo(sessionId); 442 assertThat(sessionInfo).isStagedSessionFailed(); 443 } 444 445 @Test testStagedInstallDowngrade_DowngradeRequested_Commit()446 public void testStagedInstallDowngrade_DowngradeRequested_Commit() throws Exception { 447 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 448 Install.single(TestApp.A2).commit(); 449 int sessionId = stageDowngradeSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 450 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2); 451 assertSessionReady(sessionId); 452 storeSessionId(sessionId); 453 } 454 455 @Test testStagedInstallDowngrade_DowngradeRequested_Fails_Commit()456 public void testStagedInstallDowngrade_DowngradeRequested_Fails_Commit() throws Exception { 457 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 458 Install.single(TestApp.A2).commit(); 459 stageDowngradeSingleApk(TestApp.A1).assertFailure(); 460 } 461 462 @Test testStagedInstallDowngrade_DowngradeRequested_DebugBuild_VerifyPostReboot()463 public void testStagedInstallDowngrade_DowngradeRequested_DebugBuild_VerifyPostReboot() 464 throws Exception { 465 int sessionId = retrieveLastSessionId(); 466 assertSessionApplied(sessionId); 467 // App should be downgraded. 468 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1); 469 } 470 471 @Test testInstallStagedApex_Commit()472 public void testInstallStagedApex_Commit() throws Exception { 473 BroadcastCounter counter = new BroadcastCounter(PackageInstaller.ACTION_SESSION_COMMITTED); 474 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 475 int sessionId = stageSingleApk(TestApp.Apex2).assertSuccessful().getSessionId(); 476 assertSessionReady(sessionId); 477 storeSessionId(sessionId); 478 // Version shouldn't change before reboot. 479 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 480 counter.assertNoBroadcastReceived(); 481 } 482 483 @Test testInstallStagedApex_VerifyPostReboot()484 public void testInstallStagedApex_VerifyPostReboot() throws Exception { 485 BroadcastCounter counter = new BroadcastCounter(PackageInstaller.ACTION_SESSION_COMMITTED); 486 int sessionId = retrieveLastSessionId(); 487 assertSessionApplied(sessionId); 488 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 489 counter.assertNoBroadcastReceived(); 490 } 491 492 @Test testInstallStagedApexAndApk_Commit()493 public void testInstallStagedApexAndApk_Commit() throws Exception { 494 BroadcastCounter counter = new BroadcastCounter(PackageInstaller.ACTION_SESSION_COMMITTED); 495 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 496 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 497 int sessionId = stageMultipleApks(TestApp.Apex2, TestApp.A1) 498 .assertSuccessful().getSessionId(); 499 assertSessionReady(sessionId); 500 storeSessionId(sessionId); 501 // Version shouldn't change before reboot. 502 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 503 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 504 counter.assertNoBroadcastReceived(); 505 } 506 507 @Test testInstallStagedApexAndApk_VerifyPostReboot()508 public void testInstallStagedApexAndApk_VerifyPostReboot() throws Exception { 509 BroadcastCounter counter = new BroadcastCounter(PackageInstaller.ACTION_SESSION_COMMITTED); 510 int sessionId = retrieveLastSessionId(); 511 assertSessionApplied(sessionId); 512 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 513 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1); 514 counter.assertNoBroadcastReceived(); 515 } 516 517 @Test testsFailsNonStagedApexInstall()518 public void testsFailsNonStagedApexInstall() throws Exception { 519 try { 520 SystemUtil.runShellCommandForNoOutput("pm bypass-staged-installer-check true"); 521 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 522 TestApp apex = new TestApp( 523 "Apex2", SHIM_APEX_PACKAGE_NAME, 2, /*isApex*/true, 524 "com.android.apex.cts.shim.v2.apex"); 525 InstallUtils.commitExpectingFailure(AssertionError.class, 526 "does not support non-staged update", 527 Install.single(apex)); 528 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 529 } finally { 530 SystemUtil.runShellCommandForNoOutput("pm bypass-staged-installer-check false"); 531 } 532 } 533 534 @Test testInstallStagedNonPreInstalledApex_Fails()535 public void testInstallStagedNonPreInstalledApex_Fails() throws Exception { 536 assertThat(getInstalledVersion(NOT_PRE_INSTALL_APEX_PACKAGE_NAME)).isEqualTo(-1); 537 int sessionId = stageSingleApk(ApexNotPreInstalled).assertFailure().getSessionId(); 538 PackageInstaller.SessionInfo sessionInfo = getSessionInfo(sessionId); 539 assertThat(sessionInfo).isStagedSessionFailed(); 540 } 541 542 @Test testInstallStagedDifferentPackageNameWithInstalledApex_Fails()543 public void testInstallStagedDifferentPackageNameWithInstalledApex_Fails() throws Exception { 544 assertThat(getInstalledVersion(DIFFERENT_APEX_PACKAGE_NAME)).isEqualTo(-1); 545 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 546 int sessionId = stageSingleApk(Apex2DifferentPackageName).assertFailure().getSessionId(); 547 PackageInstaller.SessionInfo sessionInfo = getSessionInfo(sessionId); 548 assertThat(sessionInfo.getStagedSessionErrorMessage()).contains( 549 "Attempting to install new APEX package"); 550 } 551 552 @Test testStageApkWithSameNameAsApexShouldFail_Commit()553 public void testStageApkWithSameNameAsApexShouldFail_Commit() throws Exception { 554 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 555 int sessionId = stageSingleApk(Apex2AsApk).assertSuccessful().getSessionId(); 556 assertSessionReady(sessionId); 557 storeSessionId(sessionId); 558 } 559 560 @Test testStageApkWithSameNameAsApexShouldFail_VerifyPostReboot()561 public void testStageApkWithSameNameAsApexShouldFail_VerifyPostReboot() throws Exception { 562 int sessionId = retrieveLastSessionId(); 563 assertSessionFailed(sessionId); 564 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 565 PackageInstaller.SessionInfo sessionInfo = getSessionInfo(sessionId); 566 assertThat(sessionInfo.getStagedSessionErrorMessage()).contains( 567 "is an APEX package and can't be installed as an APK"); 568 } 569 570 @Test testNonStagedInstallApkWithSameNameAsApexShouldFail()571 public void testNonStagedInstallApkWithSameNameAsApexShouldFail() throws Exception { 572 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 573 InstallUtils.commitExpectingFailure(AssertionError.class, 574 "is an APEX package and can't be installed as an APK", 575 Install.single(Apex2AsApk)); 576 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 577 } 578 579 @Test testInstallV2Apex_Commit()580 public void testInstallV2Apex_Commit() throws Exception { 581 int sessionId = stageSingleApk(TestApp.Apex2).assertSuccessful().getSessionId(); 582 assertSessionReady(sessionId); 583 storeSessionId(sessionId); 584 } 585 586 @Test testInstallV2Apex_VerifyPostReboot()587 public void testInstallV2Apex_VerifyPostReboot() throws Exception { 588 int sessionId = retrieveLastSessionId(); 589 assertSessionApplied(sessionId); 590 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 591 assertThat(getInstalledVersion(SHIM_PACKAGE_NAME)).isNotEqualTo(-1); 592 } 593 594 @Test testInstallV2SignedBobApex_Commit()595 public void testInstallV2SignedBobApex_Commit() throws Exception { 596 int sessionId = stageSingleApk(Apex2SignedBobRot).assertSuccessful().getSessionId(); 597 assertSessionReady(sessionId); 598 storeSessionId(sessionId); 599 } 600 601 @Test testInstallV2SignedBobApex_VerifyPostReboot()602 public void testInstallV2SignedBobApex_VerifyPostReboot() throws Exception { 603 int sessionId = retrieveLastSessionId(); 604 assertSessionApplied(sessionId); 605 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 606 } 607 608 @Test testInstallV3Apex_Commit()609 public void testInstallV3Apex_Commit() throws Exception { 610 int sessionId = stageSingleApk(TestApp.Apex3).assertSuccessful().getSessionId(); 611 assertSessionReady(sessionId); 612 storeSessionId(sessionId); 613 } 614 615 @Test testInstallV3Apex_VerifyPostReboot()616 public void testInstallV3Apex_VerifyPostReboot() throws Exception { 617 int sessionId = retrieveLastSessionId(); 618 assertSessionApplied(sessionId); 619 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3); 620 } 621 622 @Test testInstallV3SignedBobApex_Commit()623 public void testInstallV3SignedBobApex_Commit() throws Exception { 624 int sessionId = stageSingleApk(Apex2SignedBobRot).assertSuccessful().getSessionId(); 625 assertSessionReady(sessionId); 626 storeSessionId(sessionId); 627 } 628 629 @Test testInstallV3SignedBobApex_VerifyPostReboot()630 public void testInstallV3SignedBobApex_VerifyPostReboot() throws Exception { 631 int sessionId = retrieveLastSessionId(); 632 assertSessionApplied(sessionId); 633 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 634 } 635 636 @Test testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_Commit()637 public void testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_Commit() 638 throws Exception { 639 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3); 640 int sessionId = stageSingleApk(TestApp.Apex2).assertFailure().getSessionId(); 641 PackageInstaller.SessionInfo sessionInfo = getSessionInfo(sessionId); 642 assertThat(sessionInfo).isStagedSessionFailed(); 643 // Also verify that correct session info is reported by PackageManager. 644 assertSessionFailed(sessionId); 645 storeSessionId(sessionId); 646 } 647 648 @Test testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_VerifyPostReboot()649 public void testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_VerifyPostReboot() 650 throws Exception { 651 int sessionId = retrieveLastSessionId(); 652 assertSessionFailed(sessionId); 653 // INSTALL_REQUEST_DOWNGRADE wasn't set, so apex shouldn't be downgraded. 654 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3); 655 } 656 657 @Test testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_Commit()658 public void testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_Commit() 659 throws Exception { 660 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3); 661 int sessionId = stageDowngradeSingleApk(TestApp.Apex2).assertSuccessful().getSessionId(); 662 assertSessionReady(sessionId); 663 storeSessionId(sessionId); 664 } 665 666 @Test testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_VerifyPostReboot()667 public void testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_VerifyPostReboot() 668 throws Exception { 669 int sessionId = retrieveLastSessionId(); 670 assertSessionApplied(sessionId); 671 // Apex should be downgraded. 672 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 673 } 674 675 @Test testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_Commit()676 public void testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_Commit() 677 throws Exception { 678 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3); 679 int sessionId = stageDowngradeSingleApk(TestApp.Apex2).getSessionId(); 680 PackageInstaller.SessionInfo sessionInfo = getSessionInfo(sessionId); 681 assertThat(sessionInfo).isStagedSessionFailed(); 682 // Also verify that correct session info is reported by PackageManager. 683 assertSessionFailed(sessionId); 684 storeSessionId(sessionId); 685 } 686 687 @Test testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_VerifyPostReboot()688 public void testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_VerifyPostReboot() 689 throws Exception { 690 int sessionId = retrieveLastSessionId(); 691 assertSessionFailed(sessionId); 692 // Apex shouldn't be downgraded. 693 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3); 694 } 695 696 @Test testStagedInstallDowngradeApexToSystemVersion_DebugBuild_Commit()697 public void testStagedInstallDowngradeApexToSystemVersion_DebugBuild_Commit() 698 throws Exception { 699 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 700 int sessionId = stageDowngradeSingleApk(TestApp.Apex1).assertSuccessful().getSessionId(); 701 assertSessionReady(sessionId); 702 storeSessionId(sessionId); 703 } 704 705 @Test testStagedInstallDowngradeApexToSystemVersion_DebugBuild_VerifyPostReboot()706 public void testStagedInstallDowngradeApexToSystemVersion_DebugBuild_VerifyPostReboot() 707 throws Exception { 708 int sessionId = retrieveLastSessionId(); 709 assertSessionApplied(sessionId); 710 // Apex should be downgraded. 711 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 712 } 713 714 @Test testFailsInvalidApexInstall_Commit()715 public void testFailsInvalidApexInstall_Commit() throws Exception { 716 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 717 int sessionId = stageSingleApk(ApexWrongSha2).assertFailure() 718 .getSessionId(); 719 assertSessionFailed(sessionId); 720 storeSessionId(sessionId); 721 } 722 723 @Test testFailsInvalidApexInstall_AbandonSessionIsNoop()724 public void testFailsInvalidApexInstall_AbandonSessionIsNoop() throws Exception { 725 int sessionId = retrieveLastSessionId(); 726 assertSessionFailed(sessionId); 727 // Session is in a final state. Test that abandoning the session doesn't remove it from the 728 // session database. 729 abandonSession(sessionId); 730 assertSessionFailed(sessionId); 731 } 732 733 @Test testStagedApkSessionCallbacks()734 public void testStagedApkSessionCallbacks() throws Exception { 735 736 List<Integer> created = new ArrayList<Integer>(); 737 738 HandlerThread handlerThread = new HandlerThread( 739 "StagedApkSessionCallbacksTestHandlerThread"); 740 handlerThread.start(); 741 Handler handler = new Handler(handlerThread.getLooper()); 742 743 PackageInstaller.SessionCallback callback = new PackageInstaller.SessionCallback() { 744 745 @Override 746 public void onCreated(int sessionId) { 747 synchronized (created) { 748 created.add(sessionId); 749 } 750 } 751 752 @Override public void onBadgingChanged(int sessionId) { } 753 @Override public void onActiveChanged(int sessionId, boolean active) { } 754 @Override public void onProgressChanged(int sessionId, float progress) { } 755 @Override public void onFinished(int sessionId, boolean success) { } 756 }; 757 758 Context context = InstrumentationRegistry.getInstrumentation().getContext(); 759 PackageInstaller packageInstaller = getPackageInstaller(); 760 packageInstaller.registerSessionCallback(callback, handler); 761 762 int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 763 764 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 765 assertSessionReady(sessionId); 766 767 packageInstaller.unregisterSessionCallback(callback); 768 769 handlerThread.quitSafely(); 770 handlerThread.join(); 771 772 synchronized (created) { 773 assertThat(created).containsExactly(sessionId); 774 } 775 packageInstaller.abandonSession(sessionId); 776 } 777 778 @Test testInstallStagedApexWithoutApexSuffix_Commit()779 public void testInstallStagedApexWithoutApexSuffix_Commit() throws Exception { 780 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 781 782 int sessionId = stageSingleApk("com.android.apex.cts.shim.v2.apex", "package") 783 .assertSuccessful().getSessionId(); 784 assertSessionReady(sessionId); 785 storeSessionId(sessionId); 786 // Version shouldn't change before reboot. 787 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 788 } 789 790 @Test testInstallStagedApexWithoutApexSuffix_VerifyPostReboot()791 public void testInstallStagedApexWithoutApexSuffix_VerifyPostReboot() throws Exception { 792 int sessionId = retrieveLastSessionId(); 793 assertSessionApplied(sessionId); 794 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 795 } 796 797 @Test testRejectsApexDifferentCertificate()798 public void testRejectsApexDifferentCertificate() throws Exception { 799 int sessionId = stageSingleApk(Apex2DifferentCertificate) 800 .assertFailure().getSessionId(); 801 PackageInstaller.SessionInfo info = getSessionInfo(sessionId); 802 assertThat(info.getSessionId()).isEqualTo(sessionId); 803 assertThat(info).isStagedSessionFailed(); 804 assertThat(info.getStagedSessionErrorMessage()).contains("is not compatible with the one " 805 + "currently installed on device"); 806 } 807 808 /** 809 * Tests for staged install involving rotated keys. 810 * 811 * Here alice means the original default key that cts.shim.v1 package was signed with and 812 * bob is the new key alice rotates to. Where ambiguous, we will refer keys as alice and bob 813 * instead of "old key" and "new key". 814 */ 815 816 // The update should fail if it is signed with a different non-rotated key 817 @Test testUpdateWithDifferentKeyButNoRotation()818 public void testUpdateWithDifferentKeyButNoRotation() throws Exception { 819 stageSingleApk(Apex2SignedBob).assertFailure().getSessionId(); 820 } 821 822 // The update should pass if it is signed with a proper rotated key 823 @Test testUpdateWithDifferentKey_Commit()824 public void testUpdateWithDifferentKey_Commit() throws Exception { 825 stageSingleApk(Apex2SignedBobRot).assertSuccessful().getSessionId(); 826 } 827 828 @Test testUpdateWithDifferentKey_VerifyPostReboot()829 public void testUpdateWithDifferentKey_VerifyPostReboot() throws Exception { 830 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 831 } 832 833 // Once updated with a new rotated key (bob), further updates with old key (alice) should fail 834 @Test testUntrustedOldKeyIsRejected()835 public void testUntrustedOldKeyIsRejected() throws Exception { 836 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 837 stageSingleApk(TestApp.Apex3).assertFailure().getSessionId(); 838 } 839 840 // Should be able to update with an old key which is trusted 841 @Test testTrustedOldKeyIsAccepted_Commit()842 public void testTrustedOldKeyIsAccepted_Commit() throws Exception { 843 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 844 stageSingleApk(Apex2SignedBobRotRollback).assertSuccessful().getSessionId(); 845 } 846 847 @Test testTrustedOldKeyIsAccepted_CommitPostReboot()848 public void testTrustedOldKeyIsAccepted_CommitPostReboot() throws Exception { 849 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 850 stageSingleApk(TestApp.Apex3).assertSuccessful().getSessionId(); 851 } 852 853 @Test testTrustedOldKeyIsAccepted_VerifyPostReboot()854 public void testTrustedOldKeyIsAccepted_VerifyPostReboot() throws Exception { 855 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3); 856 } 857 858 // Once updated with a new rotated key (bob), further updates with new key (bob) should pass 859 @Test testAfterRotationNewKeyCanUpdateFurther_CommitPostReboot()860 public void testAfterRotationNewKeyCanUpdateFurther_CommitPostReboot() throws Exception { 861 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 862 stageSingleApk(Apex3SignedBobRot).assertSuccessful().getSessionId(); 863 } 864 865 @Test testAfterRotationNewKeyCanUpdateFurther_VerifyPostReboot()866 public void testAfterRotationNewKeyCanUpdateFurther_VerifyPostReboot() throws Exception { 867 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3); 868 } 869 870 // Once updated with a new rotated key (bob), further updates can be done with key only 871 @Test testAfterRotationNewKeyCanUpdateFurtherWithoutLineage()872 public void testAfterRotationNewKeyCanUpdateFurtherWithoutLineage() 873 throws Exception { 874 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 875 stageSingleApk(Apex3SignedBob).assertSuccessful().getSessionId(); 876 } 877 878 /** 879 * Tests for staging and installing multiple staged sessions. 880 */ 881 882 // Should fail to stage multiple sessions when check-point is not available 883 @Test testFailStagingMultipleSessionsIfNoCheckPoint()884 public void testFailStagingMultipleSessionsIfNoCheckPoint() throws Exception { 885 stageSingleApk(TestApp.A1).assertSuccessful(); 886 int sessionId = stageSingleApk(TestApp.B1).assertFailure().getSessionId(); 887 PackageInstaller.SessionInfo info = getSessionInfo(sessionId); 888 assertThat(info).isStagedSessionFailed(); 889 assertThat(info.getStagedSessionErrorMessage()).contains( 890 "Cannot stage multiple sessions without checkpoint support"); 891 } 892 893 @Test testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk()894 public void testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk() throws Exception { 895 stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 896 897 int sessionId = stageSingleApk(TestApp.A1).assertFailure().getSessionId(); 898 PackageInstaller.SessionInfo info = getSessionInfo(sessionId); 899 assertThat(info).isStagedSessionFailed(); 900 assertThat(info.getStagedSessionErrorMessage()).contains( 901 "has been staged already by session"); 902 } 903 904 @Test testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk()905 public void testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk() 906 throws Exception { 907 stageMultipleApks(TestApp.A1, TestApp.B1).assertSuccessful(); 908 stageSingleApk(TestApp.C1).assertSuccessful(); 909 } 910 911 @Test testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk()912 public void testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk() throws Exception { 913 stageMultipleApks(TestApp.A1, TestApp.B1).assertSuccessful().getSessionId(); 914 int sessionId = stageMultipleApks(TestApp.A2, TestApp.C1).assertFailure().getSessionId(); 915 PackageInstaller.SessionInfo info = getSessionInfo(sessionId); 916 assertThat(info).isStagedSessionFailed(); 917 assertThat(info.getStagedSessionErrorMessage()).contains( 918 "has been staged already by session"); 919 } 920 921 // Should succeed in installing multiple staged sessions together 922 @Test testMultipleStagedInstall_ApkOnly_Commit()923 public void testMultipleStagedInstall_ApkOnly_Commit() 924 throws Exception { 925 int firstSessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 926 int secondSessionId = stageSingleApk(TestApp.B1).assertSuccessful().getSessionId(); 927 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 928 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1); 929 storeSessionIds(Arrays.asList(firstSessionId, secondSessionId)); 930 } 931 932 @Test testMultipleStagedInstall_ApkOnly_VerifyPostReboot()933 public void testMultipleStagedInstall_ApkOnly_VerifyPostReboot() 934 throws Exception { 935 List<Integer> sessionIds = retrieveLastSessionIds(); 936 for (int sessionId: sessionIds) { 937 assertSessionApplied(sessionId); 938 } 939 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1); 940 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1); 941 } 942 943 // If apk installation fails in one staged session, then all staged session should fail. 944 @Test testInstallMultipleStagedSession_PartialFail_ApkOnly_Commit()945 public void testInstallMultipleStagedSession_PartialFail_ApkOnly_Commit() 946 throws Exception { 947 int firstSessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 948 int secondSessionId = stageSingleApk(TestApp.B1).assertSuccessful().getSessionId(); 949 950 // Install TestApp.A2 so that after reboot TestApp.A1 fails to install as it is downgrade 951 Install.single(TestApp.A2).commit(); 952 953 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2); 954 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1); 955 storeSessionIds(Arrays.asList(firstSessionId, secondSessionId)); 956 } 957 958 @Test testInstallMultipleStagedSession_PartialFail_ApkOnly_VerifyPostReboot()959 public void testInstallMultipleStagedSession_PartialFail_ApkOnly_VerifyPostReboot() 960 throws Exception { 961 List<Integer> sessionIds = retrieveLastSessionIds(); 962 for (int sessionId: sessionIds) { 963 assertSessionFailed(sessionId); 964 } 965 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2); 966 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1); 967 } 968 969 // Failure reason of staged install should be be persisted for single sessions 970 @Test testFailureReasonPersists_SingleSession_Commit()971 public void testFailureReasonPersists_SingleSession_Commit() throws Exception { 972 int sessionId = Install.single(TestApp.A1).setStaged().commit(); 973 // Install TestApp.A2 so that after reboot TestApp.A1 fails to install as it is downgrade 974 Install.single(TestApp.A2).commit(); 975 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2); 976 storeSessionId(sessionId); 977 } 978 979 @Test testFailureReasonPersists_SingleSession_VerifyPostReboot()980 public void testFailureReasonPersists_SingleSession_VerifyPostReboot() throws Exception { 981 int sessionId = retrieveLastSessionId(); 982 assertSessionFailedWithMessage(sessionId, "Failed to install sessionId: " + sessionId); 983 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2); 984 } 985 986 // If apk installation fails in one staged session, then all staged session should fail. 987 @Test testFailureReasonPersists_MultipleSession_Commit()988 public void testFailureReasonPersists_MultipleSession_Commit() throws Exception { 989 int firstSessionId = Install.single(TestApp.A1).setStaged().commit(); 990 int secondSessionId = Install.single(TestApp.B1).setStaged().commit(); 991 // Install TestApp.A2 so that after reboot TestApp.A1 fails to install as it is downgrade 992 Install.single(TestApp.A2).commit(); 993 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2); 994 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1); 995 storeSessionIds(Arrays.asList(firstSessionId, secondSessionId)); 996 } 997 998 @Test testFailureReasonPersists_MultipleSession_VerifyPostReboot()999 public void testFailureReasonPersists_MultipleSession_VerifyPostReboot() throws Exception { 1000 List<Integer> sessionIds = retrieveLastSessionIds(); 1001 int failingSessionId = sessionIds.get(0); 1002 for (int sessionId: sessionIds) { 1003 assertSessionFailedWithMessage(sessionId, "Failed to install sessionId: " 1004 + failingSessionId); 1005 } 1006 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2); 1007 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1); 1008 } 1009 1010 @Test testSamegradeSystemApex_Commit()1011 public void testSamegradeSystemApex_Commit() throws Exception { 1012 final PackageInfo shim = 1013 InstrumentationRegistry.getInstrumentation().getContext().getPackageManager() 1014 .getPackageInfo(SHIM_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX); 1015 assertThat(shim.getLongVersionCode()).isEqualTo(1); 1016 assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo( 1017 ApplicationInfo.FLAG_SYSTEM); 1018 assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo( 1019 ApplicationInfo.FLAG_INSTALLED); 1020 int sessionId = stageDowngradeSingleApk(TestApp.Apex1).assertSuccessful().getSessionId(); 1021 storeSessionId(sessionId); 1022 } 1023 1024 @Test testSamegradeSystemApex_VerifyPostReboot()1025 public void testSamegradeSystemApex_VerifyPostReboot() throws Exception { 1026 int sessionId = retrieveLastSessionId(); 1027 assertSessionApplied(sessionId); 1028 final PackageInfo shim = 1029 InstrumentationRegistry.getInstrumentation().getContext().getPackageManager() 1030 .getPackageInfo(SHIM_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX); 1031 assertThat(shim.getLongVersionCode()).isEqualTo(1); 1032 // Check that APEX on /data wins. 1033 assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) 1034 .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP); 1035 assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo( 1036 ApplicationInfo.FLAG_INSTALLED); 1037 assertThat(shim.applicationInfo.sourceDir) 1038 .isEqualTo("/data/apex/active/com.android.apex.cts.shim@1.apex"); 1039 assertThat(shim.applicationInfo.publicSourceDir).isEqualTo(shim.applicationInfo.sourceDir); 1040 } 1041 1042 @Test testInstallApkChangingFingerprint()1043 public void testInstallApkChangingFingerprint() throws Exception { 1044 int sessionId = Install.single(TestApp.A1).setStaged().commit(); 1045 storeSessionId(sessionId); 1046 } 1047 1048 @Test testInstallApkChangingFingerprint_VerifyAborted()1049 public void testInstallApkChangingFingerprint_VerifyAborted() throws Exception { 1050 int sessionId = retrieveLastSessionId(); 1051 assertSessionFailed(sessionId); 1052 } 1053 1054 @Test testInstallStagedNoHashtreeApex_Commit()1055 public void testInstallStagedNoHashtreeApex_Commit() throws Exception { 1056 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1057 int sessionId = stageSingleApk(ApexNoHashtree2).assertSuccessful().getSessionId(); 1058 assertSessionReady(sessionId); 1059 storeSessionId(sessionId); 1060 // Version shouldn't change before reboot. 1061 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1062 } 1063 1064 @Test testInstallStagedNoHashtreeApex_VerifyPostReboot()1065 public void testInstallStagedNoHashtreeApex_VerifyPostReboot() throws Exception { 1066 int sessionId = retrieveLastSessionId(); 1067 assertSessionApplied(sessionId); 1068 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 1069 // Read all files under /apex/com.android.apex.cts.shim to somewhat verify that hashtree 1070 // is not corrupted 1071 Files.walkFileTree(Paths.get("/apex/com.android.apex.cts.shim"), 1072 new SimpleFileVisitor<Path>() { 1073 1074 @Override 1075 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 1076 throws IOException { 1077 Files.readAllBytes(file); 1078 return FileVisitResult.CONTINUE; 1079 } 1080 1081 @Override 1082 public FileVisitResult visitFileFailed(Path file, IOException exc) 1083 throws IOException { 1084 if (file.endsWith("lost+found")) { 1085 return FileVisitResult.CONTINUE; 1086 } 1087 throw exc; 1088 } 1089 }); 1090 } 1091 1092 @Test testInstallStagedApex_SameGrade_NewOneWins_Commit()1093 public void testInstallStagedApex_SameGrade_NewOneWins_Commit() throws Exception { 1094 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 1095 assertThat(getInstalledVersion(SHIM_PACKAGE_NAME)).isNotEqualTo(-1); 1096 int sessionId = Install.single(Apex2WithoutApkInApex).setStaged().commit(); 1097 assertSessionReady(sessionId); 1098 storeSessionId(sessionId); 1099 } 1100 1101 @Test testInstallStagedApex_SameGrade_NewOneWins_VerifyPostReboot()1102 public void testInstallStagedApex_SameGrade_NewOneWins_VerifyPostReboot() throws Exception { 1103 int sessionId = retrieveLastSessionId(); 1104 assertSessionApplied(sessionId); 1105 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); 1106 assertThat(getInstalledVersion(SHIM_PACKAGE_NAME)).isEqualTo(-1); 1107 } 1108 1109 /** 1110 * Should fail to verify apex targeting older dev sdk 1111 */ 1112 @Test testApexTargetingOldDevSdkFailsVerification()1113 public void testApexTargetingOldDevSdkFailsVerification() throws Exception { 1114 stageSingleApk(Apex2SdkTargetP).assertFailure(); 1115 } 1116 1117 /** 1118 * Apex should fail to install if apk-in-apex fails to get scanned 1119 */ 1120 @Test testApexFailsToInstallIfApkInApexFailsToScan_Commit()1121 public void testApexFailsToInstallIfApkInApexFailsToScan_Commit() throws Exception { 1122 int sessionId = stageSingleApk(Apex2ApkInApexSdkTargetP).assertSuccessful().getSessionId(); 1123 storeSessionId(sessionId); 1124 } 1125 1126 @Test testApexFailsToInstallIfApkInApexFailsToScan_VerifyPostReboot()1127 public void testApexFailsToInstallIfApkInApexFailsToScan_VerifyPostReboot() throws Exception { 1128 int sessionId = retrieveLastSessionId(); 1129 assertSessionFailed(sessionId); 1130 assertSessionFailedWithMessage(sessionId, "Failed to parse " 1131 + "/apex/com.android.apex.cts.shim/app/CtsShimTargetPSdk"); 1132 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1133 } 1134 1135 @Test testCorruptedApexFailsVerification_b146895998()1136 public void testCorruptedApexFailsVerification_b146895998() throws Exception { 1137 int sessionId = stageSingleApk(CorruptedApex_b146895998).assertFailure().getSessionId(); 1138 PackageInstaller.SessionInfo sessionInfo = getSessionInfo(sessionId); 1139 assertThat(sessionInfo).isStagedSessionFailed(); 1140 } 1141 1142 /** 1143 * Should fail to pass apk signature check 1144 */ 1145 @Test testApexWithUnsignedApkFailsVerification()1146 public void testApexWithUnsignedApkFailsVerification() throws Exception { 1147 assertThat(stageSingleApk(Apex2NoApkSignature).getErrorMessage()) 1148 .contains("INSTALL_PARSE_FAILED_NO_CERTIFICATES"); 1149 } 1150 1151 /** 1152 * Should fail to verify apex with unsigned payload 1153 */ 1154 @Test testApexWithUnsignedPayloadFailsVerification()1155 public void testApexWithUnsignedPayloadFailsVerification() throws Exception { 1156 int sessionId = stageSingleApk(Apex2UnsignedPayload).assertFailure().getSessionId(); 1157 PackageInstaller.SessionInfo sessionInfo = getSessionInfo(sessionId); 1158 assertThat(sessionInfo).isStagedSessionFailed(); 1159 assertThat(sessionInfo.getStagedSessionErrorMessage()) 1160 .contains("AVB footer verification failed"); 1161 } 1162 1163 /** 1164 * Should fail to verify apex signed payload with a different key 1165 */ 1166 @Test testApexSignPayloadWithDifferentKeyFailsVerification()1167 public void testApexSignPayloadWithDifferentKeyFailsVerification() throws Exception { 1168 int sessionId = stageSingleApk( 1169 Apex2SignPayloadWithDifferentKey).assertFailure().getSessionId(); 1170 PackageInstaller.SessionInfo sessionInfo = getSessionInfo(sessionId); 1171 assertThat(sessionInfo).isStagedSessionFailed(); 1172 assertThat(sessionInfo.getStagedSessionErrorMessage()) 1173 .contains("public key doesn't match the pre-installed one"); 1174 } 1175 1176 /** 1177 * Test non-priv apps cannot access /data/app-staging folder contents 1178 */ 1179 @Test testAppStagingDirCannotBeReadByNonPrivApps()1180 public void testAppStagingDirCannotBeReadByNonPrivApps() throws Exception { 1181 final int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 1182 // Non-priv apps should not be able to view contents of app-staging directory 1183 final File appStagingDir = new File("/data/app-staging"); 1184 assertThat(appStagingDir.exists()).isTrue(); 1185 assertThat(appStagingDir.listFiles()).isNull(); 1186 // Non-owner user should not be able to access sub-dirs of app-staging directory 1187 final File appStagingSubDir = new File("/data/app-staging/session_" + sessionId); 1188 assertThat(appStagingSubDir.exists()).isFalse(); 1189 assertThat(appStagingDir.listFiles()).isNull(); 1190 } 1191 getInstalledVersion(String packageName)1192 private static long getInstalledVersion(String packageName) { 1193 Context context = InstrumentationRegistry.getInstrumentation().getContext(); 1194 PackageManager pm = context.getPackageManager(); 1195 try { 1196 PackageInfo info = pm.getPackageInfo(packageName, PackageManager.MATCH_APEX); 1197 return info.getLongVersionCode(); 1198 } catch (PackageManager.NameNotFoundException e) { 1199 return -1; 1200 } 1201 } 1202 1203 @Test testApexSetsUpdatedSystemAppFlag_preUpdate()1204 public void testApexSetsUpdatedSystemAppFlag_preUpdate() throws Exception { 1205 final PackageInfo info = InstallUtils.getPackageInfo(SHIM_APEX_PACKAGE_NAME); 1206 assertThat(info).isNotNull(); 1207 boolean isSystemApp = (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 1208 boolean isUpdatedSystemApp = 1209 (info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; 1210 assertThat(isSystemApp).isTrue(); 1211 assertThat(isUpdatedSystemApp).isFalse(); 1212 } 1213 1214 @Test testApexSetsUpdatedSystemAppFlag_postUpdate()1215 public void testApexSetsUpdatedSystemAppFlag_postUpdate() throws Exception { 1216 final PackageInfo info = InstallUtils.getPackageInfo(SHIM_APEX_PACKAGE_NAME); 1217 assertThat(info).isNotNull(); 1218 boolean isSystemApp = (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 1219 boolean isUpdatedSystemApp = 1220 (info.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; 1221 assertThat(isSystemApp).isTrue(); 1222 assertThat(isUpdatedSystemApp).isTrue(); 1223 } 1224 1225 @Test testRebootlessUpdate()1226 public void testRebootlessUpdate() throws Exception { 1227 InstallUtils.dropShellPermissionIdentity(); 1228 InstallUtils.adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGE_UPDATES); 1229 1230 final PackageManager pm = 1231 InstrumentationRegistry.getInstrumentation().getContext().getPackageManager(); 1232 { 1233 PackageInfo apex = pm.getPackageInfo(SHIM_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX); 1234 assertThat(apex.getLongVersionCode()).isEqualTo(1); 1235 assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 1236 .isEqualTo(ApplicationInfo.FLAG_SYSTEM); 1237 assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) 1238 .isEqualTo(ApplicationInfo.FLAG_INSTALLED); 1239 assertThat(apex.applicationInfo.sourceDir).startsWith("/system/apex"); 1240 } 1241 1242 Install.single(Apex2Rebootless).commit(); 1243 { 1244 PackageInfo apex = pm.getPackageInfo(SHIM_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX); 1245 assertThat(apex.getLongVersionCode()).isEqualTo(2); 1246 assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) 1247 .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP); 1248 assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) 1249 .isEqualTo(ApplicationInfo.FLAG_INSTALLED); 1250 assertThat(apex.applicationInfo.sourceDir).startsWith("/data/apex/active"); 1251 } 1252 { 1253 PackageInfo apex = pm.getPackageInfo(SHIM_APEX_PACKAGE_NAME, 1254 PackageManager.MATCH_APEX | PackageManager.MATCH_FACTORY_ONLY); 1255 assertThat(apex.getLongVersionCode()).isEqualTo(1); 1256 assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 1257 .isEqualTo(ApplicationInfo.FLAG_SYSTEM); 1258 assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) 1259 .isEqualTo(ApplicationInfo.FLAG_INSTALLED); 1260 assertThat(apex.applicationInfo.sourceDir).startsWith("/system/apex"); 1261 } 1262 } 1263 1264 @Test testRebootlessUpdate_installV3()1265 public void testRebootlessUpdate_installV3() throws Exception { 1266 InstallUtils.dropShellPermissionIdentity(); 1267 InstallUtils.adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGE_UPDATES); 1268 1269 final PackageManager pm = 1270 InstrumentationRegistry.getInstrumentation().getContext().getPackageManager(); 1271 1272 Install.single(Apex3Rebootless).commit(); 1273 { 1274 PackageInfo apex = pm.getPackageInfo(SHIM_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX); 1275 assertThat(apex.getLongVersionCode()).isEqualTo(3); 1276 assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) 1277 .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP); 1278 assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) 1279 .isEqualTo(ApplicationInfo.FLAG_INSTALLED); 1280 assertThat(apex.applicationInfo.sourceDir).startsWith("/data/apex/active"); 1281 } 1282 { 1283 PackageInfo apex = pm.getPackageInfo(SHIM_APEX_PACKAGE_NAME, 1284 PackageManager.MATCH_APEX | PackageManager.MATCH_FACTORY_ONLY); 1285 assertThat(apex.getLongVersionCode()).isEqualTo(1); 1286 assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) 1287 .isEqualTo(ApplicationInfo.FLAG_SYSTEM); 1288 assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) 1289 .isEqualTo(ApplicationInfo.FLAG_INSTALLED); 1290 assertThat(apex.applicationInfo.sourceDir).startsWith("/system/apex"); 1291 } 1292 } 1293 1294 @Test testRebootlessUpdate_downgradeToV2_fails()1295 public void testRebootlessUpdate_downgradeToV2_fails() throws Exception { 1296 InstallUtils.dropShellPermissionIdentity(); 1297 InstallUtils.adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGE_UPDATES); 1298 1299 final PackageManager pm = 1300 InstrumentationRegistry.getInstrumentation().getContext().getPackageManager(); 1301 1302 { 1303 PackageInfo apex = pm.getPackageInfo(SHIM_APEX_PACKAGE_NAME, PackageManager.MATCH_APEX); 1304 assertThat(apex.getLongVersionCode()).isEqualTo(3); 1305 assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) 1306 .isEqualTo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP); 1307 assertThat(apex.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) 1308 .isEqualTo(ApplicationInfo.FLAG_INSTALLED); 1309 assertThat(apex.applicationInfo.sourceDir).startsWith("/data/apex/active"); 1310 } 1311 1312 InstallUtils.commitExpectingFailure( 1313 AssertionError.class, 1314 "INSTALL_FAILED_VERSION_DOWNGRADE", 1315 Install.single(Apex2Rebootless)); 1316 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(3); 1317 } 1318 1319 @Test testRebootlessUpdate_noPermission_fails()1320 public void testRebootlessUpdate_noPermission_fails() throws Exception { 1321 InstallUtils.dropShellPermissionIdentity(); 1322 1323 InstallUtils.commitExpectingFailure(SecurityException.class, 1324 "Not allowed to perform APEX updates", 1325 Install.single(Apex2Rebootless)); 1326 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1327 } 1328 1329 @Test testRebootlessUpdate_noPreInstalledApex_fails()1330 public void testRebootlessUpdate_noPreInstalledApex_fails() throws Exception { 1331 assertThat(getInstalledVersion(DIFFERENT_APEX_PACKAGE_NAME)).isEqualTo(-1); 1332 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1333 1334 InstallUtils.commitExpectingFailure( 1335 AssertionError.class, 1336 "Attempting to install new APEX package", 1337 Install.single(Apex2DifferentPackageName)); 1338 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1339 } 1340 1341 @Test testRebootlessUpdate_unsignedPayload_fails()1342 public void testRebootlessUpdate_unsignedPayload_fails() throws Exception { 1343 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1344 1345 InstallUtils.commitExpectingFailure( 1346 AssertionError.class, 1347 "AVB footer verification failed", 1348 Install.single(Apex2UnsignedPayload)); 1349 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1350 } 1351 1352 @Test testRebootlessUpdate_payloadSignedWithDifferentKey_fails()1353 public void testRebootlessUpdate_payloadSignedWithDifferentKey_fails() throws Exception { 1354 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1355 1356 InstallUtils.commitExpectingFailure( 1357 AssertionError.class, 1358 "public key doesn't match the pre-installed one", 1359 Install.single(Apex2SignPayloadWithDifferentKey)); 1360 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1361 } 1362 1363 @Test testRebootlessUpdate_outerContainerSignedWithDifferentCert_fails()1364 public void testRebootlessUpdate_outerContainerSignedWithDifferentCert_fails() 1365 throws Exception { 1366 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1367 1368 InstallUtils.commitExpectingFailure( 1369 AssertionError.class, 1370 "APK container signature of .+ is not compatible with the one currently installed", 1371 Install.single(Apex2DifferentCertificate)); 1372 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1373 } 1374 1375 @Test testRebootlessUpdate_outerContainerUnsigned_fails()1376 public void testRebootlessUpdate_outerContainerUnsigned_fails() throws Exception { 1377 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1378 1379 InstallUtils.commitExpectingFailure( 1380 AssertionError.class, 1381 "Failed to collect certificates from", 1382 Install.single(Apex2NoApkSignature)); 1383 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1384 } 1385 1386 @Test testRebootlessUpdate_targetsOlderSdk_fails()1387 public void testRebootlessUpdate_targetsOlderSdk_fails() throws Exception { 1388 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1389 1390 InstallUtils.commitExpectingFailure( 1391 AssertionError.class, 1392 "Requires development platform P", 1393 Install.single(Apex2SdkTargetP)); 1394 assertThat(getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); 1395 } 1396 1397 @Test testGetInactiveApexFactoryPackagesAfterApexInstall_containsNoDuplicates()1398 public void testGetInactiveApexFactoryPackagesAfterApexInstall_containsNoDuplicates() 1399 throws Exception { 1400 int flags = (PackageManager.MATCH_APEX | PackageManager.MATCH_FACTORY_ONLY 1401 | PackageManager.MATCH_UNINSTALLED_PACKAGES); 1402 List<PackageInfo> packageInfos = 1403 InstrumentationRegistry.getInstrumentation().getContext().getPackageManager() 1404 .getInstalledPackages(flags); 1405 Set<String> foundPackages = new HashSet<>(); 1406 for (PackageInfo pi : packageInfos) { 1407 if (foundPackages.contains(pi.packageName)) { 1408 throw new AssertionError(pi.packageName + " is listed at least twice."); 1409 } 1410 foundPackages.add(pi.packageName); 1411 } 1412 } 1413 1414 // It becomes harder to maintain this variety of install-related helper methods. 1415 // TODO(ioffe): refactor install-related helper methods into a separate utility. createStagedSession()1416 private static int createStagedSession() throws Exception { 1417 return Install.single(TestApp.A1).setStaged().createSession(); 1418 } 1419 commitSession(int sessionId)1420 private static Intent commitSession(int sessionId) throws IOException, InterruptedException { 1421 LocalIntentSender sender = new LocalIntentSender(); 1422 InstallUtils.openPackageInstallerSession(sessionId) 1423 .commit(sender.getIntentSender()); 1424 var result = sender.pollResult(5, TimeUnit.MINUTES); 1425 if (result == null) { 1426 throw new AssertionError("Install timeout, sessionId=" + sessionId); 1427 } 1428 return result; 1429 } 1430 stageDowngradeSingleApk(TestApp testApp)1431 private static StageSessionResult stageDowngradeSingleApk(TestApp testApp) throws Exception { 1432 Log.i(TAG, "Staging a downgrade of " + testApp); 1433 int sessionId = Install.single(testApp).setStaged().setRequestDowngrade().createSession(); 1434 // Commit the session (this will start the installation workflow). 1435 Log.i(TAG, "Committing downgrade session for apk: " + testApp); 1436 Intent result = commitSession(sessionId); 1437 return new StageSessionResult(sessionId, result); 1438 } 1439 stageSingleApk(String apkFileName, String outputFileName)1440 private static StageSessionResult stageSingleApk(String apkFileName, String outputFileName) 1441 throws Exception { 1442 File tmpFile = File.createTempFile(outputFileName, null); 1443 try (InputStream is = 1444 StagedInstallTest.class.getClassLoader().getResourceAsStream(apkFileName)) { 1445 Files.copy(is, tmpFile.toPath(), StandardCopyOption.REPLACE_EXISTING); 1446 } 1447 TestApp testApp = new TestApp(tmpFile.getName(), null, -1, 1448 apkFileName.endsWith(".apex"), tmpFile); 1449 return stageSingleApk(testApp); 1450 } 1451 stageSingleApk(TestApp testApp)1452 private static StageSessionResult stageSingleApk(TestApp testApp) throws Exception { 1453 Log.i(TAG, "Staging an install of " + testApp); 1454 int sessionId = Install.single(testApp).setStaged().createSession(); 1455 // Commit the session (this will start the installation workflow). 1456 Log.i(TAG, "Committing session for apk: " + testApp); 1457 Intent result = commitSession(sessionId); 1458 return new StageSessionResult(sessionId, result); 1459 } 1460 stageMultipleApks(TestApp... testApps)1461 private static StageSessionResult stageMultipleApks(TestApp... testApps) throws Exception { 1462 Log.i(TAG, "Staging an install of " + Arrays.toString(testApps)); 1463 int multiPackageSessionId = Install.multi(testApps).setStaged().createSession(); 1464 Intent result = commitSession(multiPackageSessionId); 1465 return new StageSessionResult(multiPackageSessionId, result); 1466 } 1467 assertSessionApplied(int sessionId)1468 private static void assertSessionApplied(int sessionId) { 1469 assertSessionState(sessionId, 1470 (session) -> assertThat(session).isStagedSessionApplied()); 1471 } 1472 assertSessionReady(int sessionId)1473 private static void assertSessionReady(int sessionId) { 1474 assertSessionState(sessionId, 1475 (session) -> assertThat(session).isStagedSessionReady()); 1476 } 1477 assertSessionFailed(int sessionId)1478 private static void assertSessionFailed(int sessionId) { 1479 assertSessionState(sessionId, 1480 (session) -> assertThat(session).isStagedSessionFailed()); 1481 } 1482 assertSessionFailedWithMessage(int sessionId, String msg)1483 private static void assertSessionFailedWithMessage(int sessionId, String msg) { 1484 assertSessionState(sessionId, (session) -> { 1485 assertThat(session).isStagedSessionFailed(); 1486 assertThat(session.getStagedSessionErrorMessage()).contains(msg); 1487 }); 1488 } 1489 assertSessionState( int sessionId, Consumer<PackageInstaller.SessionInfo> assertion)1490 private static void assertSessionState( 1491 int sessionId, Consumer<PackageInstaller.SessionInfo> assertion) { 1492 PackageInstaller packageInstaller = getPackageInstaller(); 1493 1494 List<PackageInstaller.SessionInfo> sessions = packageInstaller.getStagedSessions(); 1495 boolean found = false; 1496 for (PackageInstaller.SessionInfo session : sessions) { 1497 if (session.getSessionId() == sessionId) { 1498 assertion.accept(session); 1499 found = true; 1500 } 1501 } 1502 assertWithMessage("Expecting to find session in getStagedSession()") 1503 .that(found).isTrue(); 1504 1505 // Test also that getSessionInfo correctly returns the session. 1506 PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId); 1507 assertion.accept(sessionInfo); 1508 } 1509 storeSessionId(int sessionId)1510 private void storeSessionId(int sessionId) throws Exception { 1511 try (BufferedWriter writer = new BufferedWriter(new FileWriter(mTestStateFile))) { 1512 writer.write("" + sessionId); 1513 } 1514 } 1515 retrieveLastSessionId()1516 private int retrieveLastSessionId() throws Exception { 1517 try (BufferedReader reader = new BufferedReader(new FileReader(mTestStateFile))) { 1518 return Integer.parseInt(reader.readLine()); 1519 } 1520 } 1521 storeSessionIds(List<Integer> sessionIds)1522 private void storeSessionIds(List<Integer> sessionIds) throws Exception { 1523 try (BufferedWriter writer = new BufferedWriter(new FileWriter(mTestStateFile))) { 1524 writer.write(sessionIds.toString()); 1525 } 1526 } 1527 retrieveLastSessionIds()1528 private List<Integer> retrieveLastSessionIds() throws Exception { 1529 try (BufferedReader reader = new BufferedReader(new FileReader(mTestStateFile))) { 1530 String line = reader.readLine(); 1531 String[] sessionIdsStr = line.substring(1, line.length() - 1).split(", "); 1532 ArrayList<Integer> result = new ArrayList<>(); 1533 for (String sessionIdStr: sessionIdsStr) { 1534 result.add(Integer.parseInt(sessionIdStr)); 1535 } 1536 return result; 1537 } 1538 } 1539 1540 // TODO(ioffe): not really-tailored to staged install, rename to InstallResult? 1541 private static final class StageSessionResult { 1542 private final int sessionId; 1543 private final Intent result; 1544 StageSessionResult(int sessionId, Intent result)1545 private StageSessionResult(int sessionId, Intent result) { 1546 this.sessionId = sessionId; 1547 this.result = result; 1548 } 1549 getSessionId()1550 public int getSessionId() { 1551 return sessionId; 1552 } 1553 getErrorMessage()1554 public String getErrorMessage() { 1555 return extractErrorMessage(result); 1556 } 1557 assertSuccessful()1558 public StageSessionResult assertSuccessful() { 1559 assertStatusSuccess(result); 1560 return this; 1561 } 1562 assertFailure()1563 public StageSessionResult assertFailure() { 1564 assertStatusFailure(result); 1565 return this; 1566 } 1567 } 1568 1569 /** 1570 * Counts the number of broadcast intents received for a given type during the test. 1571 * Used by to check no broadcast intents were received during the test. 1572 */ 1573 private static class BroadcastCounter extends BroadcastReceiver { 1574 private final Context mContext; 1575 private final AtomicInteger mNumBroadcastReceived = new AtomicInteger(); 1576 BroadcastCounter(String action)1577 BroadcastCounter(String action) { 1578 mContext = InstrumentationRegistry.getInstrumentation().getContext(); 1579 mContext.registerReceiver(this, new IntentFilter(action), 1580 Context.RECEIVER_EXPORTED_UNAUDITED); 1581 } 1582 1583 @Override onReceive(Context context, Intent intent)1584 public void onReceive(Context context, Intent intent) { 1585 mNumBroadcastReceived.incrementAndGet(); 1586 } 1587 1588 /** 1589 * Waits for a while and checks no broadcasts are received. 1590 */ assertNoBroadcastReceived()1591 void assertNoBroadcastReceived() { 1592 try { 1593 // Sleep for a reasonable amount of time and check no broadcast is received 1594 Thread.sleep(TimeUnit.SECONDS.toMillis(10)); 1595 } catch (InterruptedException ignore) { 1596 } 1597 mContext.unregisterReceiver(this); 1598 assertThat(mNumBroadcastReceived.get()).isEqualTo(0); 1599 } 1600 } 1601 extractErrorMessage(Intent result)1602 private static String extractErrorMessage(Intent result) { 1603 int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, 1604 PackageInstaller.STATUS_FAILURE); 1605 if (status == -1) { 1606 throw new AssertionError("PENDING USER ACTION"); 1607 } 1608 if (status == 0) { 1609 throw new AssertionError("Result was successful"); 1610 } 1611 return result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE); 1612 } 1613 abandonSession(int sessionId)1614 private static void abandonSession(int sessionId) { 1615 getPackageInstaller().abandonSession(sessionId); 1616 } 1617 getSessionInfo(int sessionId)1618 private static PackageInstaller.SessionInfo getSessionInfo(int sessionId) { 1619 return getPackageInstaller().getSessionInfo(sessionId); 1620 } 1621 isAuto()1622 private static boolean isAuto() { 1623 var pm = InstrumentationRegistry.getInstrumentation().getContext().getPackageManager(); 1624 return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); 1625 } 1626 } 1627