1 /* 2 * Copyright (C) 2015 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.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE; 20 import static android.content.pm.PackageManager.INSTALL_ALLOW_TEST; 21 import static android.content.pm.PackageManager.INSTALL_REPLACE_EXISTING; 22 23 import static com.android.managedprovisioning.task.InstallPackageTask.ERROR_INSTALLATION_FAILED; 24 import static com.android.managedprovisioning.task.InstallPackageTask.ERROR_PACKAGE_INVALID; 25 26 import static org.mockito.ArgumentMatchers.anyLong; 27 import static org.mockito.Matchers.any; 28 import static org.mockito.Matchers.anyInt; 29 import static org.mockito.Matchers.anyString; 30 import static org.mockito.Matchers.eq; 31 import static org.mockito.Mockito.never; 32 import static org.mockito.Mockito.timeout; 33 import static org.mockito.Mockito.verify; 34 import static org.mockito.Mockito.verifyNoMoreInteractions; 35 import static org.mockito.Mockito.when; 36 37 import android.app.admin.DevicePolicyManager; 38 import android.content.BroadcastReceiver; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.IntentFilter; 42 import android.content.IntentSender; 43 import android.content.pm.PackageInstaller; 44 import android.content.pm.PackageManager; 45 import android.os.Process; 46 import android.os.UserHandle; 47 import android.test.AndroidTestCase; 48 import android.test.suitebuilder.annotation.SmallTest; 49 50 import com.android.managedprovisioning.common.SettingsFacade; 51 import com.android.managedprovisioning.model.ProvisioningParams; 52 53 import org.mockito.ArgumentCaptor; 54 import org.mockito.Mock; 55 import org.mockito.MockitoAnnotations; 56 import org.mockito.stubbing.Answer; 57 58 import java.io.File; 59 import java.io.FileOutputStream; 60 import java.io.IOException; 61 import java.io.OutputStream; 62 import java.util.Arrays; 63 64 public class InstallPackageTaskTest extends AndroidTestCase { 65 private static final String TEST_PACKAGE_NAME = "com.android.test"; 66 private static final String OTHER_PACKAGE_NAME = "com.android.other"; 67 private static final ProvisioningParams TEST_PARAMS = new ProvisioningParams.Builder() 68 .setDeviceAdminPackageName(TEST_PACKAGE_NAME) 69 .setProvisioningAction(ACTION_PROVISION_MANAGED_DEVICE) 70 .build(); 71 private static final int TEST_USER_ID = 123; 72 private static final byte[] APK_CONTENT = new byte[]{'t', 'e', 's', 't'}; 73 private static final long TIMEOUT = 10000; 74 75 private static int sSessionId = 0; 76 77 @Mock private Context mMockContext; 78 @Mock private PackageManager mPackageManager; 79 @Mock private PackageInstaller mPackageInstaller; 80 @Mock private PackageInstaller.Session mSession; 81 @Mock private OutputStream mSessionWriteStream; 82 @Mock private DevicePolicyManager mDpm; 83 @Mock private AbstractProvisioningTask.Callback mCallback; 84 @Mock private DownloadPackageTask mDownloadPackageTask; 85 private InstallPackageTask mTask; 86 private final SettingsFacade mSettingsFacade = new SettingsFacadeStub(); 87 private String mTestPackageLocation; 88 89 @Override 90 protected void setUp() throws Exception { 91 super.setUp(); 92 // this is necessary for mockito to work 93 System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString()); 94 MockitoAnnotations.initMocks(this); 95 96 when(mMockContext.getPackageManager()).thenReturn(mPackageManager); 97 when(mMockContext.getPackageName()).thenReturn(getContext().getPackageName()); 98 when(mPackageManager.getPackageInstaller()).thenReturn(mPackageInstaller); 99 when(mPackageInstaller.createSession(any(PackageInstaller.SessionParams.class))).thenAnswer( 100 (Answer<Integer>) invocation -> sSessionId++); 101 when(mPackageInstaller.openSession(anyInt())).thenReturn(mSession); 102 when(mSession.openWrite(anyString(), anyLong(), anyLong())).thenReturn(mSessionWriteStream); 103 when(mMockContext.registerReceiver(any(BroadcastReceiver.class), 104 any(IntentFilter.class))).thenAnswer( 105 (Answer<Intent>) invocation -> (Intent) getContext().registerReceiver( 106 invocation.getArgument(0), invocation.getArgument(1))); 107 when(mMockContext.getSystemServiceName(eq(DevicePolicyManager.class))) 108 .thenReturn(Context.DEVICE_POLICY_SERVICE); 109 when(mMockContext.getSystemService(eq(Context.DEVICE_POLICY_SERVICE))).thenReturn(mDpm); 110 when(mMockContext.getUser()).thenReturn(Process.myUserHandle()); 111 when(mMockContext.getUserId()).thenReturn(UserHandle.myUserId()); 112 113 mTestPackageLocation = File.createTempFile("test", "apk").getPath(); 114 try (FileOutputStream out = new FileOutputStream(mTestPackageLocation)) { 115 out.write(APK_CONTENT); 116 } 117 118 mTask = new InstallPackageTask(mSettingsFacade, mDownloadPackageTask, mMockContext, 119 TEST_PARAMS, mCallback); 120 } 121 122 @SmallTest 123 public void testNoDownloadLocation() { 124 // GIVEN no package was downloaded 125 when(mDownloadPackageTask.getDownloadedPackageLocation()).thenReturn(null); 126 127 // WHEN running the InstallPackageTask without specifying an install location 128 mTask.run(TEST_USER_ID); 129 // THEN no package is installed, but we get a success callback 130 verify(mPackageManager, never()).getPackageInstaller(); 131 verify(mCallback).onSuccess(mTask); 132 verifyNoMoreInteractions(mCallback); 133 assertTrue(mSettingsFacade.isPackageVerifierEnabled(mMockContext)); 134 } 135 136 @SmallTest 137 public void testSuccess() throws Exception { 138 // GIVEN a package was downloaded to TEST_LOCATION 139 when(mDownloadPackageTask.getDownloadedPackageLocation()).thenReturn(mTestPackageLocation); 140 141 // WHEN running the InstallPackageTask specifying an install location 142 mTask.run(TEST_USER_ID); 143 144 // THEN package installed is invoked with an install observer 145 IntentSender observer = verifyPackageInstalled(INSTALL_REPLACE_EXISTING); 146 147 // WHEN the package installed callback is invoked with success 148 Intent fillIn = new Intent(); 149 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, TEST_PACKAGE_NAME); 150 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_SUCCESS); 151 observer.sendIntent(getContext(), 0, fillIn, null, null); 152 153 // THEN we receive a success callback 154 verify(mCallback, timeout(TIMEOUT)).onSuccess(mTask); 155 verifyNoMoreInteractions(mCallback); 156 assertTrue(mSettingsFacade.isPackageVerifierEnabled(mMockContext)); 157 } 158 159 @SmallTest 160 public void testSuccess_allowTestOnly() throws Exception { 161 // GIVEN a package was downloaded to TEST_LOCATION 162 when(mDownloadPackageTask.getDownloadedPackageLocation()).thenReturn(mTestPackageLocation); 163 // WHEN package to be installed is the current device owner. 164 when(mDpm.isDeviceOwnerApp(eq(TEST_PACKAGE_NAME))).thenReturn(true); 165 166 // WHEN running the InstallPackageTask specifying an install location 167 mTask.run(TEST_USER_ID); 168 169 // THEN package installed is invoked with an install observer 170 IntentSender observer = verifyPackageInstalled( 171 INSTALL_REPLACE_EXISTING | INSTALL_ALLOW_TEST); 172 173 // WHEN the package installed callback is invoked with success 174 Intent fillIn = new Intent(); 175 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, TEST_PACKAGE_NAME); 176 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_SUCCESS); 177 observer.sendIntent(getContext(), 0, fillIn, null, null); 178 179 // THEN we receive a success callback 180 verify(mCallback, timeout(TIMEOUT)).onSuccess(mTask); 181 verifyNoMoreInteractions(mCallback); 182 assertTrue(mSettingsFacade.isPackageVerifierEnabled(mMockContext)); 183 } 184 185 186 @SmallTest 187 public void testInstallFailedVersionDowngrade() throws Exception { 188 // GIVEN a package was downloaded to TEST_LOCATION 189 when(mDownloadPackageTask.getDownloadedPackageLocation()).thenReturn(mTestPackageLocation); 190 191 // WHEN running the InstallPackageTask with a package already at a higher version 192 mTask.run(TEST_USER_ID); 193 194 // THEN package installed is invoked with an install observer 195 IntentSender observer = verifyPackageInstalled(INSTALL_REPLACE_EXISTING); 196 197 // WHEN the package installed callback is invoked with version downgrade error 198 Intent fillIn = new Intent(); 199 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, TEST_PACKAGE_NAME); 200 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); 201 fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, 202 PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE); 203 observer.sendIntent(getContext(), 0, fillIn, null, null); 204 205 // THEN we get a success callback, because an existing version of the DPC is present 206 verify(mCallback, timeout(TIMEOUT)).onSuccess(mTask); 207 verifyNoMoreInteractions(mCallback); 208 assertTrue(mSettingsFacade.isPackageVerifierEnabled(mMockContext)); 209 } 210 211 @SmallTest 212 public void testInstallFailedOtherError() throws Exception { 213 // GIVEN a package was downloaded to TEST_LOCATION 214 when(mDownloadPackageTask.getDownloadedPackageLocation()).thenReturn(mTestPackageLocation); 215 216 // WHEN running the InstallPackageTask with a package already at a higher version 217 mTask.run(TEST_USER_ID); 218 219 // THEN package installed is invoked with an install observer 220 IntentSender observer = verifyPackageInstalled(INSTALL_REPLACE_EXISTING); 221 222 // WHEN the package installed callback is invoked with version invalid apk error 223 Intent fillIn = new Intent(); 224 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, TEST_PACKAGE_NAME); 225 fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); 226 fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, 227 PackageManager.INSTALL_FAILED_INVALID_APK); 228 observer.sendIntent(getContext(), 0, fillIn, null, null); 229 230 // THEN we get a success callback, because an existing version of the DPC is present 231 verify(mCallback, timeout(TIMEOUT)).onError(mTask, ERROR_INSTALLATION_FAILED); 232 verifyNoMoreInteractions(mCallback); 233 assertTrue(mSettingsFacade.isPackageVerifierEnabled(mMockContext)); 234 } 235 236 @SmallTest 237 public void testDifferentPackageName() throws Exception { 238 // GIVEN a package was downloaded to TEST_LOCATION 239 when(mDownloadPackageTask.getDownloadedPackageLocation()).thenReturn(mTestPackageLocation); 240 241 // WHEN running the InstallPackageTask with a package already at a higher version 242 mTask.run(TEST_USER_ID); 243 244 // THEN package installed is invoked with an install observer 245 IntentSender observer = verifyPackageInstalled(INSTALL_REPLACE_EXISTING); 246 247 // WHEN the package installed callback is invoked with the wrong package 248 Intent fillIn = new Intent(); 249 fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, OTHER_PACKAGE_NAME); 250 observer.sendIntent(getContext(), 0, fillIn, null, null); 251 252 // THEN we get a success callback, because the wrong package name 253 verify(mCallback, timeout(TIMEOUT)).onError(mTask, ERROR_PACKAGE_INVALID); 254 verifyNoMoreInteractions(mCallback); 255 assertTrue(mSettingsFacade.isPackageVerifierEnabled(mContext)); 256 } 257 258 private IntentSender verifyPackageInstalled(int installFlags) throws IOException { 259 ArgumentCaptor<PackageInstaller.SessionParams> paramsCaptor 260 = ArgumentCaptor.forClass(PackageInstaller.SessionParams.class); 261 ArgumentCaptor<byte[]> fileContentCaptor = ArgumentCaptor.forClass(byte[].class); 262 263 verify(mPackageInstaller).createSession(paramsCaptor.capture()); 264 assertEquals(installFlags, paramsCaptor.getValue().installFlags); 265 verify(mSessionWriteStream).write(fileContentCaptor.capture(), eq(0), 266 eq(APK_CONTENT.length)); 267 assertTrue(Arrays.equals(APK_CONTENT, 268 Arrays.copyOf(fileContentCaptor.getValue(), APK_CONTENT.length))); 269 270 ArgumentCaptor<IntentSender> intentSenderCaptor 271 = ArgumentCaptor.forClass(IntentSender.class); 272 273 // THEN package installation was started and we will receive a status callback 274 verify(mSession).commit(intentSenderCaptor.capture()); 275 return intentSenderCaptor.getValue(); 276 } 277 278 private static class SettingsFacadeStub extends SettingsFacade { 279 private boolean mPackageVerifierEnabled = true; 280 281 @Override 282 public boolean isPackageVerifierEnabled(Context c) { 283 return mPackageVerifierEnabled; 284 } 285 286 @Override 287 public void setPackageVerifierEnabled(Context c, boolean packageVerifierEnabled) { 288 mPackageVerifierEnabled = packageVerifierEnabled; 289 } 290 } 291 } 292