1 /* 2 * Copyright (C) 2021 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.managedprovisioning.task; 18 19 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; 20 import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE; 21 22 import static com.android.managedprovisioning.task.InstallPackageTask.ERROR_INSTALLATION_FAILED; 23 24 import static com.google.common.truth.Truth.assertThat; 25 26 import static org.junit.Assert.fail; 27 28 import android.app.Instrumentation; 29 import android.content.Context; 30 import android.os.UserHandle; 31 32 import androidx.test.InstrumentationRegistry; 33 import androidx.test.filters.SmallTest; 34 35 import com.android.bedstead.permissions.annotations.EnsureHasPermission; 36 import com.android.bedstead.testapp.TestApp; 37 import com.android.bedstead.testapp.TestAppProvider; 38 import com.android.compatibility.common.util.BlockingCallback; 39 import com.android.managedprovisioning.model.ProvisioningParams; 40 41 import org.junit.Before; 42 import org.junit.Ignore; 43 import org.junit.Test; 44 45 import java.io.File; 46 import java.io.IOException; 47 import java.io.PrintWriter; 48 49 50 @SmallTest 51 public class InstallPackageTaskTest { 52 53 private final static int RESULT_SUCCESS = -1; 54 private final static int USER_ID = 0; 55 private final static UserHandle USER_HANDLE = new UserHandle(USER_ID); 56 private final static Instrumentation sInstrumentation = 57 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation(); 58 private final static Context sContext = InstrumentationRegistry.getTargetContext(); 59 private final static String TEST_PACKAGE_NAME = "test.package.name"; 60 private static final String INVALID_FILE_CONTENTS = "invalid contents"; 61 private static final PackageLocationProvider FILE_NULL_PATH_PROVIDER = () -> null; 62 private static final PackageLocationProvider FILE_INVALID_PATH_PROVIDER = 63 () -> new File("invalid/path"); 64 private static final String TEST_APP = "com.android.bedstead.testapp.EmptyTestApp"; 65 66 private InstallPackageBlockingCallback mInstallPackageBlockingCallback; 67 68 @Before setUp()69 public void setUp() { 70 mInstallPackageBlockingCallback = new InstallPackageBlockingCallback(); 71 } 72 73 @EnsureHasPermission(WRITE_EXTERNAL_STORAGE) 74 @Test run_success()75 public void run_success() throws IOException, InterruptedException { 76 File appToInstallFile = getAppToInstallFile(); 77 TestApp testApp = writeApkToInstall(appToInstallFile); 78 InstallPackageTask task = new InstallPackageTask( 79 () -> appToInstallFile, 80 sContext, 81 createProvisioningParams(testApp.packageName()), 82 mInstallPackageBlockingCallback, 83 testApp.packageName()); 84 int resultCode; 85 86 try { 87 sInstrumentation.runOnMainSync(() -> task.run(USER_ID)); 88 resultCode = mInstallPackageBlockingCallback.await(); 89 } finally { 90 // TODO(b/191277673): Use TestApp#uninstall(UserHandle) when available to use. 91 } 92 93 assertThat(resultCode).isEqualTo(RESULT_SUCCESS); 94 assertFileDeleted(appToInstallFile); 95 } 96 97 @Test run_fileIsNull_success()98 public void run_fileIsNull_success() throws InterruptedException { 99 InstallPackageTask task = new InstallPackageTask( 100 FILE_NULL_PATH_PROVIDER, 101 sContext, 102 createProvisioningParams(TEST_PACKAGE_NAME), 103 mInstallPackageBlockingCallback, 104 TEST_PACKAGE_NAME); 105 106 task.run(USER_ID); 107 int resultCode = mInstallPackageBlockingCallback.await(); 108 109 assertThat(resultCode).isEqualTo(RESULT_SUCCESS); 110 } 111 112 @Test run_fileDoesNotExist_fail()113 public void run_fileDoesNotExist_fail() throws InterruptedException { 114 InstallPackageTask task = new InstallPackageTask( 115 FILE_INVALID_PATH_PROVIDER, 116 sContext, 117 createProvisioningParams(TEST_PACKAGE_NAME), 118 mInstallPackageBlockingCallback, 119 TEST_PACKAGE_NAME); 120 121 sInstrumentation.runOnMainSync(() -> task.run(USER_ID)); 122 int resultCode = mInstallPackageBlockingCallback.await(); 123 124 assertThat(resultCode).isEqualTo(ERROR_INSTALLATION_FAILED); 125 } 126 127 @Ignore("b/191285670") 128 @EnsureHasPermission(WRITE_EXTERNAL_STORAGE) 129 @Test run_packageAlreadyInstalled_success()130 public void run_packageAlreadyInstalled_success() throws IOException, InterruptedException { 131 File appToInstallFile = getAppToInstallFile(); 132 TestApp testApp = writeApkToInstall(appToInstallFile); 133 testApp.install(USER_HANDLE); 134 InstallPackageTask task = new InstallPackageTask( 135 () -> appToInstallFile, 136 sContext, 137 createProvisioningParams(testApp.packageName()), 138 mInstallPackageBlockingCallback, 139 testApp.packageName()); 140 int resultCode; 141 142 try { 143 sInstrumentation.runOnMainSync(() -> task.run(USER_ID)); 144 resultCode = mInstallPackageBlockingCallback.await(); 145 } finally { 146 // TODO(b/191277673): Use TestApp#uninstall(UserHandle) when available to use. 147 } 148 149 assertThat(resultCode).isEqualTo(RESULT_SUCCESS); 150 assertFileDeleted(appToInstallFile); 151 } 152 153 @EnsureHasPermission(WRITE_EXTERNAL_STORAGE) 154 @Test run_invalidApkFile_error()155 public void run_invalidApkFile_error() throws IOException, InterruptedException { 156 File appToInstallFile = getAppToInstallFile(); 157 writeInvalidFile(appToInstallFile); 158 InstallPackageTask task = new InstallPackageTask( 159 () -> appToInstallFile, 160 sContext, 161 createProvisioningParams(TEST_PACKAGE_NAME), 162 mInstallPackageBlockingCallback, 163 TEST_PACKAGE_NAME); 164 int resultCode; 165 166 sInstrumentation.runOnMainSync(() -> task.run(USER_ID)); 167 resultCode = mInstallPackageBlockingCallback.await(); 168 169 assertThat(resultCode).isEqualTo(ERROR_INSTALLATION_FAILED); 170 assertFileDeleted(appToInstallFile); 171 } 172 assertFileDeleted(File appToInstallFile)173 private void assertFileDeleted(File appToInstallFile) { 174 if (appToInstallFile.exists()) { 175 appToInstallFile.delete(); 176 fail("File " + appToInstallFile.getAbsolutePath() + " was not deleted."); 177 } 178 } 179 writeInvalidFile(File appToInstallFile)180 private void writeInvalidFile(File appToInstallFile) throws IOException { 181 try (PrintWriter writer = new PrintWriter(appToInstallFile)) { 182 writer.println(INVALID_FILE_CONTENTS); 183 } 184 } 185 createProvisioningParams(String packageName)186 private static ProvisioningParams createProvisioningParams(String packageName) { 187 return new ProvisioningParams.Builder() 188 .setProvisioningAction(ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE) 189 .setDeviceAdminPackageName(packageName) 190 .build(); 191 } 192 writeApkToInstall(File appToInstallFile)193 private static TestApp writeApkToInstall(File appToInstallFile) throws IOException { 194 TestAppProvider testAppProvider = new TestAppProvider(); 195 TestApp testApp = testAppProvider 196 .query() 197 // TODO(b/192330233): We use this specific app as it does not have the testOnly flag 198 .wherePackageName().isEqualTo(TEST_APP) 199 .get(); 200 testApp.writeApkFile(appToInstallFile); 201 return testApp; 202 } 203 getAppToInstallFile()204 private static File getAppToInstallFile() throws IOException { 205 return File.createTempFile( 206 /* prefix= */ "test_app" + Math.random() * Integer.MAX_VALUE, 207 ".apk", 208 sContext.getCacheDir()); 209 } 210 211 private static class InstallPackageBlockingCallback extends BlockingCallback<Integer> 212 implements AbstractProvisioningTask.Callback { 213 @Override onSuccess(AbstractProvisioningTask task)214 public void onSuccess(AbstractProvisioningTask task) { 215 callbackTriggered(RESULT_SUCCESS); 216 } 217 218 @Override onError( AbstractProvisioningTask task, int errorCode, String errorMessage)219 public void onError( 220 AbstractProvisioningTask task, int errorCode, String errorMessage) { 221 callbackTriggered(errorCode); 222 } 223 } 224 } 225