Home | History | Annotate | Download | only in task
      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