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.getPackageInstaller; 20 import static com.android.tests.stagedinstall.PackageInstallerSessionInfoSubject.assertThat; 21 22 import static com.google.common.truth.Truth.assertThat; 23 import static com.google.common.truth.Truth.assertWithMessage; 24 25 import static org.junit.Assert.fail; 26 27 import android.Manifest; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.pm.ApplicationInfo; 31 import android.content.pm.PackageInfo; 32 import android.content.pm.PackageInstaller; 33 import android.content.pm.PackageManager; 34 import android.os.Handler; 35 import android.os.HandlerThread; 36 import android.os.storage.StorageManager; 37 import android.util.Log; 38 39 import androidx.test.platform.app.InstrumentationRegistry; 40 41 import com.android.cts.install.lib.Install; 42 import com.android.cts.install.lib.InstallUtils; 43 import com.android.cts.install.lib.LocalIntentSender; 44 import com.android.cts.install.lib.TestApp; 45 import com.android.cts.install.lib.Uninstall; 46 47 import org.junit.After; 48 import org.junit.Before; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.junit.runners.JUnit4; 52 53 import java.io.BufferedReader; 54 import java.io.BufferedWriter; 55 import java.io.File; 56 import java.io.FileReader; 57 import java.io.FileWriter; 58 import java.io.IOException; 59 import java.io.InputStream; 60 import java.io.OutputStream; 61 import java.nio.file.FileVisitResult; 62 import java.nio.file.Files; 63 import java.nio.file.Path; 64 import java.nio.file.Paths; 65 import java.nio.file.SimpleFileVisitor; 66 import java.nio.file.attribute.BasicFileAttributes; 67 import java.time.Duration; 68 import java.util.ArrayList; 69 import java.util.Arrays; 70 import java.util.List; 71 import java.util.concurrent.TimeUnit; 72 import java.util.function.Consumer; 73 import java.util.stream.Collectors; 74 75 /** 76 * This series of tests are meant to be driven by a host, since some of the interactions being 77 * tested require the device to be rebooted, and some assertions to be verified post-reboot. 78 * The convention used here (not enforced) is that the test methods in this file will be named 79 * the same way as the test methods in the "host" class (see e.g. 80 * {@code com.android.test.stagedinstall.host.StagedInstallTest}), with an optional suffix preceded 81 * by an underscore, in case of multiple phases. 82 * Example: 83 * - In {@code com.android.test.stagedinstall.host.StagedInstallTest}: 84 * 85 * @Test 86 * public void testInstallStagedApk() throws Exception { 87 * runPhase("testInstallStagedApk_Commit"); 88 * getDevice().reboot(); 89 * runPhase("testInstallStagedApk_VerifyPostReboot"); 90 * } 91 * - In this class: 92 * @Test public void testInstallStagedApk_Commit() throws Exception; 93 * @Test public void testInstallStagedApk_VerifyPostReboot() throws Exception; 94 */ 95 @RunWith(JUnit4.class) 96 public class StagedInstallTest { 97 98 private static final String TAG = "StagedInstallTest"; 99 100 private File mTestStateFile = new File( 101 InstrumentationRegistry.getInstrumentation().getContext().getFilesDir(), 102 "ctsstagedinstall_state"); 103 104 private static final Duration WAIT_FOR_SESSION_REMOVED_TTL = Duration.ofSeconds(10); 105 private static final Duration SLEEP_DURATION = Duration.ofMillis(200); 106 107 private static final String SHIM_PACKAGE_NAME = "com.android.apex.cts.shim"; 108 private static final TestApp TESTAPP_SAME_NAME_AS_APEX = new TestApp( 109 "TestAppSamePackageNameAsApex", SHIM_PACKAGE_NAME, 1, /*isApex*/ false, 110 "StagedInstallTestAppSamePackageNameAsApex.apk"); 111 public static final TestApp Apex2DifferentCertificate = new TestApp( 112 "Apex2DifferentCertificate", SHIM_PACKAGE_NAME, 2, /*isApex*/true, 113 "com.android.apex.cts.shim.v2_different_certificate.apex"); 114 private static final TestApp Apex2SignedBob = new TestApp( 115 "Apex2SignedBob", SHIM_PACKAGE_NAME, 2, /*isApex*/true, 116 "com.android.apex.cts.shim.v2_signed_bob.apex"); 117 private static final TestApp Apex2SignedBobRot = new TestApp( 118 "Apex2SignedBobRot", SHIM_PACKAGE_NAME, 2, /*isApex*/true, 119 "com.android.apex.cts.shim.v2_signed_bob_rot.apex"); 120 private static final TestApp Apex2SignedBobRotRollback = new TestApp( 121 "Apex2SignedBobRotRollback", SHIM_PACKAGE_NAME, 2, /*isApex*/true, 122 "com.android.apex.cts.shim.v2_signed_bob_rot_rollback.apex"); 123 private static final TestApp Apex3SignedBob = new TestApp( 124 "Apex3SignedBob", SHIM_PACKAGE_NAME, 3, /*isApex*/true, 125 "com.android.apex.cts.shim.v3_signed_bob.apex"); 126 private static final TestApp Apex3SignedBobRot = new TestApp( 127 "Apex3SignedBobRot", SHIM_PACKAGE_NAME, 3, /*isApex*/true, 128 "com.android.apex.cts.shim.v3_signed_bob_rot.apex"); 129 130 @Before adoptShellPermissions()131 public void adoptShellPermissions() { 132 InstrumentationRegistry 133 .getInstrumentation() 134 .getUiAutomation() 135 .adoptShellPermissionIdentity( 136 Manifest.permission.INSTALL_PACKAGES, 137 Manifest.permission.DELETE_PACKAGES); 138 } 139 140 @After dropShellPermissions()141 public void dropShellPermissions() { 142 InstrumentationRegistry 143 .getInstrumentation() 144 .getUiAutomation() 145 .dropShellPermissionIdentity(); 146 } 147 148 @Before clearBroadcastReceiver()149 public void clearBroadcastReceiver() { 150 SessionUpdateBroadcastReceiver.sessionBroadcasts.clear(); 151 } 152 153 // This is marked as @Test to take advantage of @Before/@After methods of this class. Actual 154 // purpose of this method to be called before and after each test case of 155 // com.android.test.stagedinstall.host.StagedInstallTest to reduce tests flakiness. 156 @Test cleanUp()157 public void cleanUp() throws Exception { 158 PackageInstaller packageInstaller = getPackageInstaller(); 159 List<PackageInstaller.SessionInfo> stagedSessions = packageInstaller.getStagedSessions(); 160 for (PackageInstaller.SessionInfo sessionInfo : stagedSessions) { 161 if (sessionInfo.getParentSessionId() != PackageInstaller.SessionInfo.INVALID_ID) { 162 // Cannot abandon a child session 163 continue; 164 } 165 try { 166 Log.i(TAG, "abandoning session " + sessionInfo.getSessionId()); 167 packageInstaller.abandonSession(sessionInfo.getSessionId()); 168 } catch (Exception e) { 169 Log.e(TAG, "Failed to abandon session " + sessionInfo.getSessionId(), e); 170 } 171 } 172 Uninstall.packages(TestApp.A, TestApp.B); 173 Files.deleteIfExists(mTestStateFile.toPath()); 174 } 175 176 @Test testFailInstallIfNoPermission()177 public void testFailInstallIfNoPermission() throws Exception { 178 dropShellPermissions(); 179 try { 180 createStagedSession(); 181 fail(); // Should have thrown SecurityException. 182 } catch (SecurityException e) { 183 // This would be a better version, but it requires a version of truth not present in the 184 // tree yet. 185 // assertThat(e).hasMessageThat().containsMatch(...); 186 assertThat(e.getMessage()).containsMatch( 187 "Neither user [0-9]+ nor current process has " 188 + "android.permission.INSTALL_PACKAGES"); 189 } 190 } 191 192 @Test testInstallStagedApk_Commit()193 public void testInstallStagedApk_Commit() throws Exception { 194 int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 195 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 196 waitForIsReadyBroadcast(sessionId); 197 assertSessionReady(sessionId); 198 storeSessionId(sessionId); 199 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 200 } 201 202 @Test testInstallStagedApk_VerifyPostReboot()203 public void testInstallStagedApk_VerifyPostReboot() throws Exception { 204 int sessionId = retrieveLastSessionId(); 205 assertSessionApplied(sessionId); 206 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1); 207 } 208 209 @Test testInstallStagedApk_AbandonSessionIsNoop()210 public void testInstallStagedApk_AbandonSessionIsNoop() throws Exception { 211 int sessionId = retrieveLastSessionId(); 212 assertSessionApplied(sessionId); 213 // Session is in a final state. Test that abandoning the session doesn't remove it from the 214 // session database. 215 abandonSession(sessionId); 216 assertSessionApplied(sessionId); 217 } 218 219 @Test testInstallMultipleStagedApks_Commit()220 public void testInstallMultipleStagedApks_Commit() throws Exception { 221 int sessionId = stageMultipleApks(TestApp.A1, TestApp.B1) 222 .assertSuccessful().getSessionId(); 223 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 224 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1); 225 waitForIsReadyBroadcast(sessionId); 226 assertSessionReady(sessionId); 227 storeSessionId(sessionId); 228 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 229 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1); 230 } 231 232 @Test testInstallMultipleStagedApks_VerifyPostReboot()233 public void testInstallMultipleStagedApks_VerifyPostReboot() throws Exception { 234 int sessionId = retrieveLastSessionId(); 235 assertSessionApplied(sessionId); 236 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1); 237 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1); 238 } 239 240 @Test testAbandonStagedApkBeforeReboot_CommitAndAbandon()241 public void testAbandonStagedApkBeforeReboot_CommitAndAbandon() throws Exception { 242 int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 243 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 244 waitForIsReadyBroadcast(sessionId); 245 PackageInstaller.SessionInfo session = getStagedSessionInfo(sessionId); 246 assertSessionReady(sessionId); 247 abandonSession(sessionId); 248 assertThat(getStagedSessionInfo(sessionId)).isNull(); 249 // Allow the session to be removed from PackageInstaller 250 Duration spentWaiting = Duration.ZERO; 251 while (spentWaiting.compareTo(WAIT_FOR_SESSION_REMOVED_TTL) < 0) { 252 session = getSessionInfo(sessionId); 253 if (session == null) { 254 Log.i(TAG, "Done waiting after " + spentWaiting); 255 break; 256 } 257 try { 258 Thread.sleep(SLEEP_DURATION.toMillis()); 259 spentWaiting = spentWaiting.plus(SLEEP_DURATION); 260 } catch (InterruptedException e) { 261 Thread.currentThread().interrupt(); 262 throw new RuntimeException(e); 263 } 264 } 265 assertThat(session).isNull(); 266 } 267 268 @Test testAbandonStagedApkBeforeReboot_VerifyPostReboot()269 public void testAbandonStagedApkBeforeReboot_VerifyPostReboot() throws Exception { 270 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 271 } 272 273 @Test testGetActiveStagedSessions()274 public void testGetActiveStagedSessions() throws Exception { 275 PackageInstaller packageInstaller = getPackageInstaller(); 276 int firstSessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 277 // Currently abandoning a session before pre-reboot verification finishes might result in 278 // a system_server crash. Before that issue is resolved we need to manually wait for 279 // pre-reboot verification to finish before abandoning sessions. 280 // TODO(b/145925842): remove following line after fixing the bug. 281 waitForIsReadyBroadcast(firstSessionId); 282 int secondSessionId = stageSingleApk(TestApp.B1).assertSuccessful().getSessionId(); 283 // Currently abandoning a session before pre-reboot verification finishes might result in 284 // a system_server crash. Before that issue is resolved we need to manually wait for 285 // pre-reboot verification to finish before abandoning sessions. 286 // TODO(b/145925842): remove following line after fixing the bug. 287 waitForIsReadyBroadcast(secondSessionId); 288 List<Integer> stagedSessionIds = getPackageInstaller().getActiveStagedSessions() 289 .stream().map(s -> s.getSessionId()).collect(Collectors.toList()); 290 assertThat(stagedSessionIds).containsExactly(firstSessionId, secondSessionId); 291 292 // Verify no other session is considered as active staged session 293 List<PackageInstaller.SessionInfo> allSessions = packageInstaller.getAllSessions(); 294 for (PackageInstaller.SessionInfo session : allSessions) { 295 if (session.isStagedSessionActive()) { 296 assertThat(stagedSessionIds).contains(session.getSessionId()); 297 } 298 } 299 } 300 301 /** 302 * Verifies that active staged session fulfils conditions stated at 303 * {@link PackageInstaller.SessionInfo#isStagedSessionActive} 304 */ 305 @Test testIsStagedSessionActive()306 public void testIsStagedSessionActive() throws Exception { 307 PackageInstaller packageInstaller = getPackageInstaller(); 308 int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 309 // Currently abandoning a session before pre-reboot verification finishes might result in 310 // a system_server crash. Before that issue is resolved we need to manually wait for 311 // pre-reboot verification to finish before abandoning sessions. 312 // TODO(b/145925842): remove following two lines after fixing the bug. 313 waitForIsReadyBroadcast(sessionId); 314 315 List<PackageInstaller.SessionInfo> allSessions = packageInstaller.getAllSessions(); 316 boolean activeStagedSessionFound = false; 317 for (PackageInstaller.SessionInfo session : allSessions) { 318 // If it fulfils conditions, then it should be an active staged session 319 if (session.isStaged() && session.isCommitted() && !session.isStagedSessionApplied() 320 && !session.isStagedSessionFailed()) { 321 activeStagedSessionFound = true; 322 assertThat(session.getSessionId()).isEqualTo(sessionId); 323 assertThat(session.isStagedSessionActive()).isTrue(); 324 } else { 325 // Otherwise, it should not be marked as active staged session 326 assertThat(session.getSessionId()).isNotEqualTo(sessionId); 327 assertThat(session.isStagedSessionActive()).isFalse(); 328 } 329 } 330 assertWithMessage("Did not find any active staged session") 331 .that(activeStagedSessionFound).isTrue(); 332 } 333 334 @Test testGetActiveStagedSessionsNoSessionActive()335 public void testGetActiveStagedSessionsNoSessionActive() throws Exception { 336 PackageInstaller packageInstaller = getPackageInstaller(); 337 List<PackageInstaller.SessionInfo> sessions = packageInstaller.getActiveStagedSessions(); 338 assertThat(sessions).isEmpty(); 339 } 340 341 @Test testGetActiveStagedSessions_MultiApkSession()342 public void testGetActiveStagedSessions_MultiApkSession() throws Exception { 343 int firstSessionId = stageMultipleApks(TestApp.A1, TestApp.B1) 344 .assertSuccessful().getSessionId(); 345 // Currently abandoning a session before pre-reboot verification finishes might result in 346 // a system_server crash. Before that issue is resolved we need to manually wait for 347 // pre-reboot verification to finish before abandoning sessions. 348 // TODO(b/145925842): remove following line after fixing the bug. 349 waitForIsReadyBroadcast(firstSessionId); 350 int secondSessionId = stageMultipleApks(TestApp.C1) 351 .assertSuccessful().getSessionId(); 352 // Currently abandoning a session before pre-reboot verification finishes might result in 353 // a system_server crash. Before that issue is resolved we need to manually wait for 354 // pre-reboot verification to finish before abandoning sessions. 355 // TODO(b/145925842): remove following line after fixing the bug. 356 waitForIsReadyBroadcast(secondSessionId); 357 List<Integer> stagedSessionIds = getPackageInstaller().getActiveStagedSessions() 358 .stream().map(s -> s.getSessionId()).collect(Collectors.toList()); 359 assertThat(stagedSessionIds).containsExactly(firstSessionId, secondSessionId); 360 } 361 362 @Test testStagedInstallDowngrade_DowngradeNotRequested_Fails_Commit()363 public void testStagedInstallDowngrade_DowngradeNotRequested_Fails_Commit() throws Exception { 364 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 365 Install.single(TestApp.A2).commit(); 366 int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 367 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2); 368 PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId); 369 assertThat(sessionInfo).isStagedSessionFailed(); 370 } 371 372 @Test testStagedInstallDowngrade_DowngradeRequested_Commit()373 public void testStagedInstallDowngrade_DowngradeRequested_Commit() throws Exception { 374 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 375 Install.single(TestApp.A2).commit(); 376 int sessionId = stageDowngradeSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 377 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2); 378 waitForIsReadyBroadcast(sessionId); 379 assertSessionReady(sessionId); 380 storeSessionId(sessionId); 381 } 382 383 @Test testStagedInstallDowngrade_DowngradeRequested_Fails_Commit()384 public void testStagedInstallDowngrade_DowngradeRequested_Fails_Commit() throws Exception { 385 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 386 Install.single(TestApp.A2).commit(); 387 int sessionId = stageDowngradeSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 388 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2); 389 PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId); 390 assertThat(sessionInfo).isStagedSessionFailed(); 391 } 392 393 @Test testStagedInstallDowngrade_DowngradeRequested_DebugBuild_VerifyPostReboot()394 public void testStagedInstallDowngrade_DowngradeRequested_DebugBuild_VerifyPostReboot() 395 throws Exception { 396 int sessionId = retrieveLastSessionId(); 397 assertSessionApplied(sessionId); 398 // App should be downgraded. 399 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1); 400 } 401 402 @Test testInstallStagedApex_Commit()403 public void testInstallStagedApex_Commit() throws Exception { 404 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1); 405 int sessionId = stageSingleApk(TestApp.Apex2).assertSuccessful().getSessionId(); 406 waitForIsReadyBroadcast(sessionId); 407 assertSessionReady(sessionId); 408 storeSessionId(sessionId); 409 // Version shouldn't change before reboot. 410 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1); 411 } 412 413 @Test testInstallStagedApex_VerifyPostReboot()414 public void testInstallStagedApex_VerifyPostReboot() throws Exception { 415 int sessionId = retrieveLastSessionId(); 416 assertSessionApplied(sessionId); 417 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2); 418 } 419 420 @Test testInstallStagedApexAndApk_Commit()421 public void testInstallStagedApexAndApk_Commit() throws Exception { 422 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1); 423 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 424 int sessionId = stageMultipleApks(TestApp.Apex2, TestApp.A1) 425 .assertSuccessful().getSessionId(); 426 waitForIsReadyBroadcast(sessionId); 427 assertSessionReady(sessionId); 428 storeSessionId(sessionId); 429 // Version shouldn't change before reboot. 430 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1); 431 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 432 } 433 434 @Test testInstallStagedApexAndApk_VerifyPostReboot()435 public void testInstallStagedApexAndApk_VerifyPostReboot() throws Exception { 436 int sessionId = retrieveLastSessionId(); 437 assertSessionApplied(sessionId); 438 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2); 439 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1); 440 } 441 442 @Test testsFailsNonStagedApexInstall()443 public void testsFailsNonStagedApexInstall() throws Exception { 444 PackageInstaller installer = getPackageInstaller(); 445 PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( 446 PackageInstaller.SessionParams.MODE_FULL_INSTALL); 447 params.setInstallAsApex(); 448 try { 449 installer.createSession(params); 450 fail("IllegalArgumentException expected"); 451 } catch (IllegalArgumentException expected) { 452 assertThat(expected.getMessage()).contains( 453 "APEX files can only be installed as part of a staged session"); 454 } 455 } 456 457 @Test testInstallStagedNonPreInstalledApex_Fails()458 public void testInstallStagedNonPreInstalledApex_Fails() throws Exception { 459 assertThat(getInstalledVersion(TestApp.NotPreInstalledApex)).isEqualTo(-1); 460 int sessionId = stageSingleApk( 461 TestApp.ApexNotPreInstalled) 462 .assertSuccessful().getSessionId(); 463 PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId); 464 assertThat(sessionInfo).isStagedSessionFailed(); 465 } 466 467 @Test testStageApkWithSameNameAsApexShouldFail_Commit()468 public void testStageApkWithSameNameAsApexShouldFail_Commit() throws Exception { 469 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1); 470 int sessionId = stageSingleApk(TESTAPP_SAME_NAME_AS_APEX) 471 .assertSuccessful().getSessionId(); 472 waitForIsReadyBroadcast(sessionId); 473 assertSessionReady(sessionId); 474 storeSessionId(sessionId); 475 } 476 477 @Test testStageApkWithSameNameAsApexShouldFail_VerifyPostReboot()478 public void testStageApkWithSameNameAsApexShouldFail_VerifyPostReboot() throws Exception { 479 int sessionId = retrieveLastSessionId(); 480 assertSessionFailed(sessionId); 481 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1); 482 } 483 484 @Test testNonStagedInstallApkWithSameNameAsApexShouldFail()485 public void testNonStagedInstallApkWithSameNameAsApexShouldFail() throws Exception { 486 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1); 487 InstallUtils.commitExpectingFailure(AssertionError.class, 488 "is an APEX package and can't be installed as an APK", 489 Install.single(TESTAPP_SAME_NAME_AS_APEX)); 490 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1); 491 } 492 493 @Test testInstallV2Apex_Commit()494 public void testInstallV2Apex_Commit() throws Exception { 495 int sessionId = stageSingleApk(TestApp.Apex2).assertSuccessful().getSessionId(); 496 waitForIsReadyBroadcast(sessionId); 497 assertSessionReady(sessionId); 498 storeSessionId(sessionId); 499 } 500 501 @Test testInstallV2Apex_VerifyPostReboot()502 public void testInstallV2Apex_VerifyPostReboot() throws Exception { 503 int sessionId = retrieveLastSessionId(); 504 assertSessionApplied(sessionId); 505 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2); 506 } 507 508 @Test testInstallV2SignedBobApex_Commit()509 public void testInstallV2SignedBobApex_Commit() throws Exception { 510 int sessionId = stageSingleApk(Apex2SignedBobRot).assertSuccessful().getSessionId(); 511 waitForIsReadyBroadcast(sessionId); 512 assertSessionReady(sessionId); 513 storeSessionId(sessionId); 514 } 515 516 @Test testInstallV2SignedBobApex_VerifyPostReboot()517 public void testInstallV2SignedBobApex_VerifyPostReboot() throws Exception { 518 int sessionId = retrieveLastSessionId(); 519 assertSessionApplied(sessionId); 520 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2); 521 } 522 523 @Test testInstallV3Apex_Commit()524 public void testInstallV3Apex_Commit() throws Exception { 525 int sessionId = stageSingleApk(TestApp.Apex3).assertSuccessful().getSessionId(); 526 waitForIsReadyBroadcast(sessionId); 527 assertSessionReady(sessionId); 528 storeSessionId(sessionId); 529 } 530 531 @Test testInstallV3Apex_VerifyPostReboot()532 public void testInstallV3Apex_VerifyPostReboot() throws Exception { 533 int sessionId = retrieveLastSessionId(); 534 assertSessionApplied(sessionId); 535 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3); 536 } 537 538 @Test testInstallV3SignedBobApex_Commit()539 public void testInstallV3SignedBobApex_Commit() throws Exception { 540 int sessionId = stageSingleApk(Apex2SignedBobRot).assertSuccessful().getSessionId(); 541 waitForIsReadyBroadcast(sessionId); 542 assertSessionReady(sessionId); 543 storeSessionId(sessionId); 544 } 545 546 @Test testInstallV3SignedBobApex_VerifyPostReboot()547 public void testInstallV3SignedBobApex_VerifyPostReboot() throws Exception { 548 int sessionId = retrieveLastSessionId(); 549 assertSessionApplied(sessionId); 550 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2); 551 } 552 553 @Test testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_Commit()554 public void testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_Commit() 555 throws Exception { 556 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3); 557 int sessionId = stageSingleApk(TestApp.Apex2).assertSuccessful().getSessionId(); 558 PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId); 559 assertThat(sessionInfo).isStagedSessionFailed(); 560 // Also verify that correct session info is reported by PackageManager. 561 assertSessionFailed(sessionId); 562 storeSessionId(sessionId); 563 } 564 565 @Test testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_VerifyPostReboot()566 public void testStagedInstallDowngradeApex_DowngradeNotRequested_Fails_VerifyPostReboot() 567 throws Exception { 568 int sessionId = retrieveLastSessionId(); 569 assertSessionFailed(sessionId); 570 // INSTALL_REQUEST_DOWNGRADE wasn't set, so apex shouldn't be downgraded. 571 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3); 572 } 573 574 @Test testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_Commit()575 public void testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_Commit() 576 throws Exception { 577 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3); 578 int sessionId = stageDowngradeSingleApk(TestApp.Apex2).assertSuccessful().getSessionId(); 579 waitForIsReadyBroadcast(sessionId); 580 assertSessionReady(sessionId); 581 storeSessionId(sessionId); 582 } 583 584 @Test testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_VerifyPostReboot()585 public void testStagedInstallDowngradeApex_DowngradeRequested_DebugBuild_VerifyPostReboot() 586 throws Exception { 587 int sessionId = retrieveLastSessionId(); 588 assertSessionApplied(sessionId); 589 // Apex should be downgraded. 590 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2); 591 } 592 593 @Test testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_Commit()594 public void testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_Commit() 595 throws Exception { 596 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3); 597 int sessionId = stageDowngradeSingleApk(TestApp.Apex2).assertSuccessful().getSessionId(); 598 PackageInstaller.SessionInfo sessionInfo = waitForBroadcast(sessionId); 599 assertThat(sessionInfo).isStagedSessionFailed(); 600 // Also verify that correct session info is reported by PackageManager. 601 assertSessionFailed(sessionId); 602 storeSessionId(sessionId); 603 } 604 605 @Test testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_VerifyPostReboot()606 public void testStagedInstallDowngradeApex_DowngradeRequested_UserBuild_Fails_VerifyPostReboot() 607 throws Exception { 608 int sessionId = retrieveLastSessionId(); 609 assertSessionFailed(sessionId); 610 // Apex shouldn't be downgraded. 611 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(3); 612 } 613 614 @Test testStagedInstallDowngradeApexToSystemVersion_DebugBuild_Commit()615 public void testStagedInstallDowngradeApexToSystemVersion_DebugBuild_Commit() 616 throws Exception { 617 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2); 618 int sessionId = stageDowngradeSingleApk(TestApp.Apex1).assertSuccessful().getSessionId(); 619 waitForIsReadyBroadcast(sessionId); 620 assertSessionReady(sessionId); 621 storeSessionId(sessionId); 622 } 623 624 @Test testStagedInstallDowngradeApexToSystemVersion_DebugBuild_VerifyPostReboot()625 public void testStagedInstallDowngradeApexToSystemVersion_DebugBuild_VerifyPostReboot() 626 throws Exception { 627 int sessionId = retrieveLastSessionId(); 628 assertSessionApplied(sessionId); 629 // Apex should be downgraded. 630 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1); 631 } 632 633 @Test testInstallApex_DeviceDoesNotSupportApex_Fails()634 public void testInstallApex_DeviceDoesNotSupportApex_Fails() throws Exception { 635 InstallUtils.commitExpectingFailure(IllegalArgumentException.class, 636 "This device doesn't support the installation of APEX files", 637 Install.single(TestApp.Apex2).setStaged()); 638 } 639 640 @Test testFailsInvalidApexInstall_Commit()641 public void testFailsInvalidApexInstall_Commit() throws Exception { 642 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1); 643 int sessionId = stageSingleApk(TestApp.ApexWrongSha2).assertSuccessful() 644 .getSessionId(); 645 waitForIsFailedBroadcast(sessionId); 646 assertSessionFailed(sessionId); 647 storeSessionId(sessionId); 648 } 649 650 @Test testFailsInvalidApexInstall_AbandonSessionIsNoop()651 public void testFailsInvalidApexInstall_AbandonSessionIsNoop() throws Exception { 652 int sessionId = retrieveLastSessionId(); 653 assertSessionFailed(sessionId); 654 // Session is in a final state. Test that abandoning the session doesn't remove it from the 655 // session database. 656 abandonSession(sessionId); 657 assertSessionFailed(sessionId); 658 } 659 660 @Test testStagedApkSessionCallbacks()661 public void testStagedApkSessionCallbacks() throws Exception { 662 663 List<Integer> created = new ArrayList<Integer>(); 664 List<Integer> finished = new ArrayList<Integer>(); 665 666 HandlerThread handlerThread = new HandlerThread( 667 "StagedApkSessionCallbacksTestHandlerThread"); 668 handlerThread.start(); 669 Handler handler = new Handler(handlerThread.getLooper()); 670 671 PackageInstaller.SessionCallback callback = new PackageInstaller.SessionCallback() { 672 673 @Override 674 public void onCreated(int sessionId) { 675 synchronized (created) { 676 created.add(sessionId); 677 } 678 } 679 680 @Override public void onBadgingChanged(int sessionId) { } 681 @Override public void onActiveChanged(int sessionId, boolean active) { } 682 @Override public void onProgressChanged(int sessionId, float progress) { } 683 684 @Override 685 public void onFinished(int sessionId, boolean success) { 686 synchronized (finished) { 687 finished.add(sessionId); 688 } 689 } 690 }; 691 692 Context context = InstrumentationRegistry.getInstrumentation().getContext(); 693 PackageInstaller packageInstaller = getPackageInstaller(); 694 packageInstaller.registerSessionCallback(callback, handler); 695 696 int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 697 698 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 699 waitForIsReadyBroadcast(sessionId); 700 assertSessionReady(sessionId); 701 702 packageInstaller.unregisterSessionCallback(callback); 703 704 handlerThread.quitSafely(); 705 handlerThread.join(); 706 707 synchronized (created) { 708 assertThat(created).containsExactly(sessionId); 709 } 710 synchronized (finished) { 711 assertThat(finished).containsExactly(sessionId); 712 } 713 packageInstaller.abandonSession(sessionId); 714 } 715 716 @Test testInstallStagedApexWithoutApexSuffix_Commit()717 public void testInstallStagedApexWithoutApexSuffix_Commit() throws Exception { 718 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1); 719 720 int sessionId = stageSingleApk("com.android.apex.cts.shim.v2.apex", "package") 721 .assertSuccessful().getSessionId(); 722 waitForIsReadyBroadcast(sessionId); 723 assertSessionReady(sessionId); 724 storeSessionId(sessionId); 725 // Version shouldn't change before reboot. 726 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1); 727 } 728 729 @Test testInstallStagedApexWithoutApexSuffix_VerifyPostReboot()730 public void testInstallStagedApexWithoutApexSuffix_VerifyPostReboot() throws Exception { 731 int sessionId = retrieveLastSessionId(); 732 assertSessionApplied(sessionId); 733 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2); 734 } 735 736 @Test testInstallStagedNoHashtreeApex_Commit()737 public void testInstallStagedNoHashtreeApex_Commit() throws Exception { 738 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1); 739 int sessionId = stageSingleApk(TestApp.ApexNoHashtree2).assertSuccessful().getSessionId(); 740 waitForIsReadyBroadcast(sessionId); 741 assertSessionReady(sessionId); 742 storeSessionId(sessionId); 743 // Version shouldn't change before reboot. 744 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1); 745 } 746 747 @Test testInstallStagedNoHashtreeApex_VerifyPostReboot()748 public void testInstallStagedNoHashtreeApex_VerifyPostReboot() throws Exception { 749 int sessionId = retrieveLastSessionId(); 750 assertSessionApplied(sessionId); 751 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2); 752 // Read all files under /apex/com.android.apex.cts.shim to somewhat verify that hashtree 753 // is not corrupted 754 Files.walkFileTree(Paths.get("/apex/com.android.apex.cts.shim"), 755 new SimpleFileVisitor<Path>() { 756 757 @Override 758 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 759 throws IOException { 760 Files.readAllBytes(file); 761 return FileVisitResult.CONTINUE; 762 } 763 764 @Override 765 public FileVisitResult visitFileFailed(Path file, IOException exc) 766 throws IOException { 767 if (file.endsWith("lost+found")) { 768 return FileVisitResult.CONTINUE; 769 } 770 throw exc; 771 } 772 }); 773 } 774 775 @Test testRejectsApexDifferentCertificate()776 public void testRejectsApexDifferentCertificate() throws Exception { 777 int sessionId = stageSingleApk(Apex2DifferentCertificate) 778 .assertSuccessful().getSessionId(); 779 PackageInstaller.SessionInfo info = 780 SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS); 781 assertThat(info.getSessionId()).isEqualTo(sessionId); 782 assertThat(info).isStagedSessionFailed(); 783 assertThat(info.getStagedSessionErrorMessage()).contains("is not compatible with the one " 784 + "currently installed on device"); 785 } 786 787 /** 788 * Tests for staged install involving rotated keys. 789 * 790 * Here alice means the original default key that cts.shim.v1 package was signed with and 791 * bob is the new key alice rotates to. Where ambiguous, we will refer keys as alice and bob 792 * instead of "old key" and "new key". 793 */ 794 795 // The update should fail if it is signed with a different non-rotated key 796 @Test testUpdateWithDifferentKeyButNoRotation()797 public void testUpdateWithDifferentKeyButNoRotation() throws Exception { 798 int sessionId = stageSingleApk(Apex2SignedBob).assertSuccessful().getSessionId(); 799 waitForIsFailedBroadcast(sessionId); 800 } 801 802 // The update should pass if it is signed with a proper rotated key 803 @Test testUpdateWithDifferentKey_Commit()804 public void testUpdateWithDifferentKey_Commit() throws Exception { 805 int sessionId = stageSingleApk(Apex2SignedBobRot).assertSuccessful().getSessionId(); 806 waitForIsReadyBroadcast(sessionId); 807 } 808 809 @Test testUpdateWithDifferentKey_VerifyPostReboot()810 public void testUpdateWithDifferentKey_VerifyPostReboot() throws Exception { 811 assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(2); 812 } 813 814 // Once updated with a new rotated key (bob), further updates with old key (alice) should fail 815 @Test testUntrustedOldKeyIsRejected()816 public void testUntrustedOldKeyIsRejected() throws Exception { 817 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2); 818 int sessionId = stageSingleApk(TestApp.Apex3).assertSuccessful().getSessionId(); 819 waitForIsFailedBroadcast(sessionId); 820 } 821 822 // Should be able to update with an old key which is trusted 823 @Test testTrustedOldKeyIsAccepted_Commit()824 public void testTrustedOldKeyIsAccepted_Commit() throws Exception { 825 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1); 826 int sessionId = stageSingleApk(Apex2SignedBobRotRollback).assertSuccessful().getSessionId(); 827 waitForIsReadyBroadcast(sessionId); 828 } 829 830 @Test testTrustedOldKeyIsAccepted_CommitPostReboot()831 public void testTrustedOldKeyIsAccepted_CommitPostReboot() throws Exception { 832 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2); 833 int sessionId = stageSingleApk(TestApp.Apex3).assertSuccessful().getSessionId(); 834 waitForIsReadyBroadcast(sessionId); 835 } 836 837 @Test testTrustedOldKeyIsAccepted_VerifyPostReboot()838 public void testTrustedOldKeyIsAccepted_VerifyPostReboot() throws Exception { 839 assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(3); 840 } 841 842 // Once updated with a new rotated key (bob), further updates with new key (bob) should pass 843 @Test testAfterRotationNewKeyCanUpdateFurther_CommitPostReboot()844 public void testAfterRotationNewKeyCanUpdateFurther_CommitPostReboot() throws Exception { 845 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2); 846 int sessionId = stageSingleApk(Apex3SignedBobRot).assertSuccessful().getSessionId(); 847 waitForIsReadyBroadcast(sessionId); 848 } 849 850 @Test testAfterRotationNewKeyCanUpdateFurther_VerifyPostReboot()851 public void testAfterRotationNewKeyCanUpdateFurther_VerifyPostReboot() throws Exception { 852 assertThat(InstallUtils.getInstalledVersion(TestApp.Apex)).isEqualTo(3); 853 } 854 855 // Once updated with a new rotated key (bob), further updates can be done with key only 856 @Test testAfterRotationNewKeyCanUpdateFurtherWithoutLineage()857 public void testAfterRotationNewKeyCanUpdateFurtherWithoutLineage() 858 throws Exception { 859 assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(2); 860 int sessionId = stageSingleApk(Apex3SignedBob).assertSuccessful().getSessionId(); 861 waitForIsReadyBroadcast(sessionId); 862 } 863 864 /** 865 * Tests for staging and installing multiple staged sessions. 866 */ 867 868 // Should fail to stage multiple sessions when check-point is not available 869 @Test testFailStagingMultipleSessionsIfNoCheckPoint()870 public void testFailStagingMultipleSessionsIfNoCheckPoint() throws Exception { 871 stageSingleApk(TestApp.A1).assertSuccessful(); 872 StageSessionResult failedSessionResult = stageSingleApk(TestApp.B1); 873 assertThat(failedSessionResult.getErrorMessage()).contains( 874 "Cannot stage multiple sessions without checkpoint support"); 875 } 876 877 @Test testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk()878 public void testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk() throws Exception { 879 stageSingleApk(TestApp.A1).assertSuccessful(); 880 StageSessionResult failedSessionResult = stageSingleApk(TestApp.A1); 881 assertThat(failedSessionResult.getErrorMessage()).contains( 882 "has been staged already by session"); 883 } 884 885 @Test testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk()886 public void testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk() 887 throws Exception { 888 stageMultipleApks(TestApp.A1, TestApp.B1).assertSuccessful(); 889 stageSingleApk(TestApp.C1).assertSuccessful(); 890 } 891 892 @Test testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk()893 public void testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk() throws Exception { 894 stageMultipleApks(TestApp.A1, TestApp.B1).assertSuccessful(); 895 StageSessionResult failedSessionResult = stageMultipleApks(TestApp.A2, TestApp.C1); 896 assertThat(failedSessionResult.getErrorMessage()).contains( 897 "has been staged already by session"); 898 } 899 900 // Should succeed in installing multiple staged sessions together 901 @Test testMultipleStagedInstall_ApkOnly_Commit()902 public void testMultipleStagedInstall_ApkOnly_Commit() 903 throws Exception { 904 int firstSessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 905 waitForIsReadyBroadcast(firstSessionId); 906 int secondSessionId = stageSingleApk(TestApp.B1).assertSuccessful().getSessionId(); 907 waitForIsReadyBroadcast(secondSessionId); 908 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 909 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1); 910 storeSessionIds(Arrays.asList(firstSessionId, secondSessionId)); 911 } 912 913 @Test testMultipleStagedInstall_ApkOnly_VerifyPostReboot()914 public void testMultipleStagedInstall_ApkOnly_VerifyPostReboot() 915 throws Exception { 916 List<Integer> sessionIds = retrieveLastSessionIds(); 917 for (int sessionId: sessionIds) { 918 assertSessionApplied(sessionId); 919 } 920 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1); 921 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1); 922 } 923 924 // If apk installation fails in one staged session, then all staged session should fail. 925 @Test testInstallMultipleStagedSession_PartialFail_ApkOnly_Commit()926 public void testInstallMultipleStagedSession_PartialFail_ApkOnly_Commit() 927 throws Exception { 928 int firstSessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId(); 929 waitForIsReadyBroadcast(firstSessionId); 930 int secondSessionId = stageSingleApk(TestApp.B1).assertSuccessful().getSessionId(); 931 waitForIsReadyBroadcast(secondSessionId); 932 933 // Install TestApp.A2 so that after reboot TestApp.A1 fails to install as it is downgrade 934 Install.single(TestApp.A2).commit(); 935 936 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2); 937 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1); 938 storeSessionIds(Arrays.asList(firstSessionId, secondSessionId)); 939 } 940 941 @Test testInstallMultipleStagedSession_PartialFail_ApkOnly_VerifyPostReboot()942 public void testInstallMultipleStagedSession_PartialFail_ApkOnly_VerifyPostReboot() 943 throws Exception { 944 List<Integer> sessionIds = retrieveLastSessionIds(); 945 for (int sessionId: sessionIds) { 946 assertSessionFailed(sessionId); 947 } 948 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2); 949 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1); 950 } 951 952 @Test testSamegradeSystemApex_Commit()953 public void testSamegradeSystemApex_Commit() throws Exception { 954 final PackageInfo shim = InstrumentationRegistry.getInstrumentation().getContext() 955 .getPackageManager().getPackageInfo(SHIM_PACKAGE_NAME, PackageManager.MATCH_APEX); 956 assertThat(shim.getLongVersionCode()).isEqualTo(1); 957 assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo( 958 ApplicationInfo.FLAG_SYSTEM); 959 assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo( 960 ApplicationInfo.FLAG_INSTALLED); 961 int sessionId = stageDowngradeSingleApk(TestApp.Apex1).assertSuccessful().getSessionId(); 962 waitForIsReadyBroadcast(sessionId); 963 storeSessionId(sessionId); 964 } 965 966 @Test testSamegradeSystemApex_VerifyPostReboot()967 public void testSamegradeSystemApex_VerifyPostReboot() throws Exception { 968 int sessionId = retrieveLastSessionId(); 969 assertSessionApplied(sessionId); 970 final PackageInfo shim = InstrumentationRegistry.getInstrumentation().getContext() 971 .getPackageManager().getPackageInfo(SHIM_PACKAGE_NAME, PackageManager.MATCH_APEX); 972 assertThat(shim.getLongVersionCode()).isEqualTo(1); 973 // Check that APEX on /data wins. 974 assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM).isEqualTo(0); 975 assertThat(shim.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED).isEqualTo( 976 ApplicationInfo.FLAG_INSTALLED); 977 } 978 979 @Test testInstallApkChangingFingerprint()980 public void testInstallApkChangingFingerprint() throws Exception { 981 int sessionId = Install.single(TestApp.A1).setStaged().commit(); 982 storeSessionId(sessionId); 983 } 984 985 @Test testInstallApkChangingFingerprint_VerifyAborted()986 public void testInstallApkChangingFingerprint_VerifyAborted() throws Exception { 987 int sessionId = retrieveLastSessionId(); 988 assertSessionFailed(sessionId); 989 } 990 getInstalledVersion(String packageName)991 private static long getInstalledVersion(String packageName) { 992 Context context = InstrumentationRegistry.getInstrumentation().getContext(); 993 PackageManager pm = context.getPackageManager(); 994 try { 995 PackageInfo info = pm.getPackageInfo(packageName, PackageManager.MATCH_APEX); 996 return info.getLongVersionCode(); 997 } catch (PackageManager.NameNotFoundException e) { 998 return -1; 999 } 1000 } 1001 1002 // It becomes harder to maintain this variety of install-related helper methods. 1003 // TODO(ioffe): refactor install-related helper methods into a separate utility. createStagedSession()1004 private static int createStagedSession() throws Exception { 1005 return Install.single(TestApp.A1).setStaged().createSession(); 1006 } 1007 commitSession(int sessionId)1008 private static void commitSession(int sessionId) throws IOException { 1009 InstallUtils.openPackageInstallerSession(sessionId) 1010 .commit(LocalIntentSender.getIntentSender()); 1011 } 1012 stageDowngradeSingleApk(TestApp testApp)1013 private static StageSessionResult stageDowngradeSingleApk(TestApp testApp) throws Exception { 1014 Log.i(TAG, "Staging a downgrade of " + testApp); 1015 int sessionId = Install.single(testApp).setStaged().setRequestDowngrade().createSession(); 1016 // Commit the session (this will start the installation workflow). 1017 Log.i(TAG, "Committing downgrade session for apk: " + testApp); 1018 commitSession(sessionId); 1019 return new StageSessionResult(sessionId, LocalIntentSender.getIntentSenderResult()); 1020 } 1021 stageSingleApk(String apkFileName, String outputFileName)1022 private static StageSessionResult stageSingleApk(String apkFileName, String outputFileName) 1023 throws Exception { 1024 Log.i(TAG, "Staging an install of " + apkFileName); 1025 // this is a trick to open an empty install session so we can manually write the package 1026 // using writeApk 1027 TestApp empty = new TestApp(null, null, -1, 1028 apkFileName.endsWith(".apex")); 1029 int sessionId = Install.single(empty).setStaged().createSession(); 1030 try (PackageInstaller.Session session = 1031 InstallUtils.openPackageInstallerSession(sessionId)) { 1032 writeApk(session, apkFileName, outputFileName); 1033 // Commit the session (this will start the installation workflow). 1034 Log.i(TAG, "Committing session for apk: " + apkFileName); 1035 commitSession(sessionId); 1036 return new StageSessionResult(sessionId, LocalIntentSender.getIntentSenderResult()); 1037 } 1038 } 1039 stageSingleApk(TestApp testApp)1040 private static StageSessionResult stageSingleApk(TestApp testApp) throws Exception { 1041 Log.i(TAG, "Staging an install of " + testApp); 1042 int sessionId = Install.single(testApp).setStaged().createSession(); 1043 // Commit the session (this will start the installation workflow). 1044 Log.i(TAG, "Committing session for apk: " + testApp); 1045 commitSession(sessionId); 1046 return new StageSessionResult(sessionId, LocalIntentSender.getIntentSenderResult()); 1047 } 1048 stageMultipleApks(TestApp... testApps)1049 private static StageSessionResult stageMultipleApks(TestApp... testApps) throws Exception { 1050 Log.i(TAG, "Staging an install of " + Arrays.toString(testApps)); 1051 int multiPackageSessionId = Install.multi(testApps).setStaged().createSession(); 1052 commitSession(multiPackageSessionId); 1053 return new StageSessionResult( 1054 multiPackageSessionId, LocalIntentSender.getIntentSenderResult()); 1055 } 1056 assertSessionApplied(int sessionId)1057 private static void assertSessionApplied(int sessionId) { 1058 assertSessionState(sessionId, 1059 (session) -> assertThat(session).isStagedSessionApplied()); 1060 } 1061 assertSessionReady(int sessionId)1062 private static void assertSessionReady(int sessionId) { 1063 assertSessionState(sessionId, 1064 (session) -> assertThat(session).isStagedSessionReady()); 1065 } 1066 assertSessionFailed(int sessionId)1067 private static void assertSessionFailed(int sessionId) { 1068 assertSessionState(sessionId, 1069 (session) -> assertThat(session).isStagedSessionFailed()); 1070 } 1071 assertSessionState( int sessionId, Consumer<PackageInstaller.SessionInfo> assertion)1072 private static void assertSessionState( 1073 int sessionId, Consumer<PackageInstaller.SessionInfo> assertion) { 1074 PackageInstaller packageInstaller = getPackageInstaller(); 1075 1076 List<PackageInstaller.SessionInfo> sessions = packageInstaller.getStagedSessions(); 1077 boolean found = false; 1078 for (PackageInstaller.SessionInfo session : sessions) { 1079 if (session.getSessionId() == sessionId) { 1080 assertion.accept(session); 1081 found = true; 1082 } 1083 } 1084 assertWithMessage("Expecting to find session in getStagedSession()") 1085 .that(found).isTrue(); 1086 1087 // Test also that getSessionInfo correctly returns the session. 1088 PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId); 1089 assertion.accept(sessionInfo); 1090 } 1091 storeSessionId(int sessionId)1092 private void storeSessionId(int sessionId) throws Exception { 1093 try (BufferedWriter writer = new BufferedWriter(new FileWriter(mTestStateFile))) { 1094 writer.write("" + sessionId); 1095 } 1096 } 1097 retrieveLastSessionId()1098 private int retrieveLastSessionId() throws Exception { 1099 try (BufferedReader reader = new BufferedReader(new FileReader(mTestStateFile))) { 1100 return Integer.parseInt(reader.readLine()); 1101 } 1102 } 1103 storeSessionIds(List<Integer> sessionIds)1104 private void storeSessionIds(List<Integer> sessionIds) throws Exception { 1105 try (BufferedWriter writer = new BufferedWriter(new FileWriter(mTestStateFile))) { 1106 writer.write(sessionIds.toString()); 1107 } 1108 } 1109 retrieveLastSessionIds()1110 private List<Integer> retrieveLastSessionIds() throws Exception { 1111 try (BufferedReader reader = new BufferedReader(new FileReader(mTestStateFile))) { 1112 String line = reader.readLine(); 1113 String[] sessionIdsStr = line.substring(1, line.length() - 1).split(", "); 1114 ArrayList<Integer> result = new ArrayList<>(); 1115 for (String sessionIdStr: sessionIdsStr) { 1116 result.add(Integer.parseInt(sessionIdStr)); 1117 } 1118 return result; 1119 } 1120 } 1121 writeApk(PackageInstaller.Session session, String apkFileName, String outputFileName)1122 private static void writeApk(PackageInstaller.Session session, String apkFileName, 1123 String outputFileName) 1124 throws Exception { 1125 try (OutputStream packageInSession = session.openWrite(outputFileName, 0, -1); 1126 InputStream is = 1127 StagedInstallTest.class.getClassLoader().getResourceAsStream(apkFileName)) { 1128 byte[] buffer = new byte[4096]; 1129 int n; 1130 while ((n = is.read(buffer)) >= 0) { 1131 packageInSession.write(buffer, 0, n); 1132 } 1133 } 1134 } 1135 1136 // TODO(ioffe): not really-tailored to staged install, rename to InstallResult? 1137 private static final class StageSessionResult { 1138 private final int sessionId; 1139 private final Intent result; 1140 StageSessionResult(int sessionId, Intent result)1141 private StageSessionResult(int sessionId, Intent result) { 1142 this.sessionId = sessionId; 1143 this.result = result; 1144 } 1145 getSessionId()1146 public int getSessionId() { 1147 return sessionId; 1148 } 1149 getErrorMessage()1150 public String getErrorMessage() { 1151 return extractErrorMessage(result); 1152 } 1153 assertSuccessful()1154 public StageSessionResult assertSuccessful() { 1155 assertStatusSuccess(result); 1156 return this; 1157 } 1158 } 1159 extractErrorMessage(Intent result)1160 private static String extractErrorMessage(Intent result) { 1161 int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, 1162 PackageInstaller.STATUS_FAILURE); 1163 if (status == -1) { 1164 throw new AssertionError("PENDING USER ACTION"); 1165 } 1166 if (status == 0) { 1167 throw new AssertionError("Result was successful"); 1168 } 1169 return result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE); 1170 } 1171 abandonSession(int sessionId)1172 private static void abandonSession(int sessionId) { 1173 getPackageInstaller().abandonSession(sessionId); 1174 } 1175 1176 /** 1177 * Returns the session by session Id, or null if no session is found. 1178 */ getStagedSessionInfo(int sessionId)1179 private static PackageInstaller.SessionInfo getStagedSessionInfo(int sessionId) { 1180 PackageInstaller packageInstaller = getPackageInstaller(); 1181 for (PackageInstaller.SessionInfo session : packageInstaller.getStagedSessions()) { 1182 if (session.getSessionId() == sessionId) { 1183 return session; 1184 } 1185 } 1186 return null; 1187 } 1188 getSessionInfo(int sessionId)1189 private static PackageInstaller.SessionInfo getSessionInfo(int sessionId) { 1190 return getPackageInstaller().getSessionInfo(sessionId); 1191 } 1192 assertStatusSuccess(Intent result)1193 private static void assertStatusSuccess(Intent result) { 1194 int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, 1195 PackageInstaller.STATUS_FAILURE); 1196 if (status == -1) { 1197 throw new AssertionError("PENDING USER ACTION"); 1198 } else if (status > 0) { 1199 String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE); 1200 throw new AssertionError(message == null ? "UNKNOWN FAILURE" : message); 1201 } 1202 } 1203 waitForIsFailedBroadcast(int sessionId)1204 private void waitForIsFailedBroadcast(int sessionId) { 1205 Log.i(TAG, "Waiting for session " + sessionId + " to be marked as failed"); 1206 try { 1207 1208 PackageInstaller.SessionInfo info = waitForBroadcast(sessionId); 1209 assertThat(info).isStagedSessionFailed(); 1210 } catch (Exception e) { 1211 throw new AssertionError(e); 1212 } 1213 } 1214 waitForIsReadyBroadcast(int sessionId)1215 private void waitForIsReadyBroadcast(int sessionId) { 1216 Log.i(TAG, "Waiting for session " + sessionId + " to be ready"); 1217 try { 1218 PackageInstaller.SessionInfo info = waitForBroadcast(sessionId); 1219 assertThat(info).isStagedSessionReady(); 1220 } catch (Exception e) { 1221 throw new AssertionError(e); 1222 } 1223 } 1224 waitForBroadcast(int sessionId)1225 private PackageInstaller.SessionInfo waitForBroadcast(int sessionId) throws Exception { 1226 PackageInstaller.SessionInfo info = 1227 SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(60, TimeUnit.SECONDS); 1228 assertWithMessage("Timed out while waiting for session to get ready") 1229 .that(info).isNotNull(); 1230 assertThat(info.getSessionId()).isEqualTo(sessionId); 1231 return info; 1232 } 1233 1234 @Test isCheckpointSupported()1235 public void isCheckpointSupported() { 1236 Context context = InstrumentationRegistry.getInstrumentation().getContext(); 1237 StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); 1238 assertThat(sm.isCheckpointSupported()).isTrue(); 1239 } 1240 } 1241