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.atomicinstall; 18 19 import static com.android.cts.install.lib.InstallUtils.assertStatusSuccess; 20 import static com.android.cts.install.lib.InstallUtils.getInstalledVersion; 21 import static com.android.cts.install.lib.InstallUtils.openPackageInstallerSession; 22 23 import static com.google.common.truth.Truth.assertThat; 24 25 import static org.junit.Assert.fail; 26 27 import android.Manifest; 28 import android.content.pm.PackageInstaller; 29 30 import androidx.test.InstrumentationRegistry; 31 32 import com.android.cts.install.lib.Install; 33 import com.android.cts.install.lib.InstallUtils; 34 import com.android.cts.install.lib.LocalIntentSender; 35 import com.android.cts.install.lib.TestApp; 36 import com.android.cts.install.lib.Uninstall; 37 38 import org.junit.After; 39 import org.junit.Before; 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 import org.junit.runners.JUnit4; 43 44 /** 45 * Tests for multi-package (a.k.a. atomic) installs. 46 */ 47 @RunWith(JUnit4.class) 48 public class AtomicInstallTest { 49 50 public static final String TEST_APP_CORRUPT_FILENAME = "corrupt.apk"; 51 private static final TestApp CORRUPT_TESTAPP = new TestApp( 52 "corrupt", "com.corrupt", 1, false, TEST_APP_CORRUPT_FILENAME); 53 adoptShellPermissions()54 private void adoptShellPermissions() { 55 InstrumentationRegistry 56 .getInstrumentation() 57 .getUiAutomation() 58 .adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGES, 59 Manifest.permission.DELETE_PACKAGES); 60 } 61 62 @Before setup()63 public void setup() throws Exception { 64 adoptShellPermissions(); 65 66 Uninstall.packages(TestApp.A, TestApp.B); 67 } 68 69 @After dropShellPermissions()70 public void dropShellPermissions() { 71 InstrumentationRegistry 72 .getInstrumentation() 73 .getUiAutomation() 74 .dropShellPermissionIdentity(); 75 } 76 77 @Test testInstallTwoApks()78 public void testInstallTwoApks() throws Exception { 79 Install.multi(TestApp.A1, TestApp.B1).commit(); 80 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(1); 81 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1); 82 } 83 84 @Test testInstallTwoApksDowngradeFail()85 public void testInstallTwoApksDowngradeFail() throws Exception { 86 Install.multi(TestApp.A2, TestApp.B1).commit(); 87 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2); 88 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1); 89 90 InstallUtils.commitExpectingFailure(AssertionError.class, 91 "INSTALL_FAILED_VERSION_DOWNGRADE", Install.multi(TestApp.A1, TestApp.B1)); 92 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(2); 93 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(1); 94 } 95 96 @Test testFailInconsistentMultiPackageCommit()97 public void testFailInconsistentMultiPackageCommit() throws Exception { 98 // Test inconsistency in staged settings 99 Install parentStaged = Install.multi(Install.single(TestApp.A1)).setStaged(); 100 Install childStaged = Install.multi(Install.single(TestApp.A1).setStaged()); 101 102 assertInconsistentStagedSettings(parentStaged); 103 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 104 assertInconsistentStagedSettings(childStaged); 105 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 106 107 // Test inconsistency in rollback settings 108 Install parentEnabledRollback = Install.multi(Install.single(TestApp.A1)) 109 .setEnableRollback(); 110 Install childEnabledRollback = Install.multi( 111 Install.single(TestApp.A1).setEnableRollback()); 112 113 assertInconsistentRollbackSettings(parentEnabledRollback); 114 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 115 assertInconsistentRollbackSettings(childEnabledRollback); 116 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 117 } 118 119 @Test testChildFailurePropagated()120 public void testChildFailurePropagated() throws Exception { 121 // Create a child session that "inherits" from a non-existent package. This 122 // causes the session commit to fail with a PackageManagerException. 123 Install childInstall = Install.single(TestApp.A1).setSessionMode( 124 PackageInstaller.SessionParams.MODE_INHERIT_EXISTING); 125 Install parentInstall = Install.multi(childInstall); 126 127 InstallUtils.commitExpectingFailure(AssertionError.class, "Missing existing base package", 128 parentInstall); 129 130 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 131 } 132 133 @Test testEarlyFailureFailsAll()134 public void testEarlyFailureFailsAll() throws Exception { 135 InstallUtils.commitExpectingFailure(AssertionError.class, "Failed to parse", 136 Install.multi(TestApp.A1, TestApp.B1, CORRUPT_TESTAPP)); 137 assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1); 138 assertThat(getInstalledVersion(TestApp.B)).isEqualTo(-1); 139 } 140 141 @Test testInvalidStateScenarios()142 public void testInvalidStateScenarios() throws Exception { 143 int parentSessionId = Install.multi(TestApp.A1, TestApp.B1).createSession(); 144 try (PackageInstaller.Session parentSession = 145 openPackageInstallerSession(parentSessionId)) { 146 for (int childSessionId : parentSession.getChildSessionIds()) { 147 try (PackageInstaller.Session childSession = 148 openPackageInstallerSession(childSessionId)) { 149 try { 150 childSession.commit(LocalIntentSender.getIntentSender()); 151 fail("Should not be able to commit a child session!"); 152 } catch (IllegalStateException e) { 153 // ignore 154 } 155 try { 156 childSession.abandon(); 157 fail("Should not be able to abandon a child session!"); 158 } catch (IllegalStateException e) { 159 // ignore 160 } 161 } 162 } 163 int toAbandonSessionId = Install.single(TestApp.A1).createSession(); 164 try (PackageInstaller.Session toAbandonSession = 165 openPackageInstallerSession(toAbandonSessionId)) { 166 toAbandonSession.abandon(); 167 try { 168 parentSession.addChildSessionId(toAbandonSessionId); 169 fail("Should not be able to add abandoned child session!"); 170 } catch (RuntimeException e) { 171 // ignore 172 } 173 } 174 175 parentSession.commit(LocalIntentSender.getIntentSender()); 176 assertStatusSuccess(LocalIntentSender.getIntentSenderResult()); 177 } 178 } 179 assertInconsistentStagedSettings(Install install)180 private static void assertInconsistentStagedSettings(Install install) { 181 assertInconsistentSettings("inconsistent staged settings", install); 182 } 183 assertInconsistentRollbackSettings(Install install)184 private static void assertInconsistentRollbackSettings(Install install) { 185 assertInconsistentSettings("inconsistent rollback settings", install); 186 } 187 assertInconsistentSettings(String failMessage, Install install)188 private static void assertInconsistentSettings(String failMessage, Install install) { 189 InstallUtils.commitExpectingFailure(AssertionError.class, failMessage, install); 190 } 191 } 192