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