• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
22 import static android.content.pm.PackageManager.INSTALL_REPLACE_EXISTING;
23 
24 import static com.android.managedprovisioning.task.InstallPackageTask.ERROR_INSTALLATION_FAILED;
25 import static com.android.managedprovisioning.task.InstallPackageTask.ERROR_PACKAGE_INVALID;
26 
27 import static org.mockito.ArgumentMatchers.anyLong;
28 import static org.mockito.Matchers.any;
29 import static org.mockito.Matchers.anyInt;
30 import static org.mockito.Matchers.anyString;
31 import static org.mockito.Matchers.eq;
32 import static org.mockito.Mockito.mock;
33 import static org.mockito.Mockito.never;
34 import static org.mockito.Mockito.timeout;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.verifyNoMoreInteractions;
37 import static org.mockito.Mockito.when;
38 
39 import android.app.admin.DevicePolicyManager;
40 import android.content.BroadcastReceiver;
41 import android.content.Context;
42 import android.content.Intent;
43 import android.content.IntentFilter;
44 import android.content.IntentSender;
45 import android.content.pm.PackageInstaller;
46 import android.content.pm.PackageManager;
47 import android.os.Process;
48 import android.os.UserHandle;
49 import android.test.AndroidTestCase;
50 import android.test.suitebuilder.annotation.SmallTest;
51 
52 import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker;
53 import com.android.managedprovisioning.model.ProvisioningParams;
54 
55 import org.mockito.ArgumentCaptor;
56 import org.mockito.Mock;
57 import org.mockito.MockitoAnnotations;
58 import org.mockito.stubbing.Answer;
59 
60 import java.io.File;
61 import java.io.FileOutputStream;
62 import java.io.IOException;
63 import java.io.OutputStream;
64 import java.util.Arrays;
65 
66 public class InstallPackageTaskTest extends AndroidTestCase {
67     private static final String TEST_PACKAGE_NAME = "com.android.test";
68     private static final String OTHER_PACKAGE_NAME = "com.android.other";
69     private static final ProvisioningParams TEST_PARAMS = new ProvisioningParams.Builder()
70             .setDeviceAdminPackageName(TEST_PACKAGE_NAME)
71             .setProvisioningAction(ACTION_PROVISION_MANAGED_DEVICE)
72             .build();
73     private static final int TEST_USER_ID = 123;
74     private static final byte[] APK_CONTENT = new byte[]{'t', 'e', 's', 't'};
75     private static final long TIMEOUT = 10000;
76 
77     private static int sSessionId = 0;
78 
79     @Mock private Context mMockContext;
80     @Mock private PackageManager mPackageManager;
81     @Mock private PackageInstaller mPackageInstaller;
82     @Mock private PackageInstaller.Session mSession;
83     @Mock private OutputStream mSessionWriteStream;
84     @Mock private DevicePolicyManager mDpm;
85     @Mock private AbstractProvisioningTask.Callback mCallback;
86     @Mock private DownloadPackageTask mDownloadPackageTask;
87     private InstallPackageTask mTask;
88     private String mTestPackageLocation;
89 
90     @Override
setUp()91     protected void setUp() throws Exception {
92         super.setUp();
93         // this is necessary for mockito to work
94         System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
95         MockitoAnnotations.initMocks(this);
96 
97         when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
98         when(mMockContext.getPackageName()).thenReturn(getContext().getPackageName());
99         when(mPackageManager.getPackageInstaller()).thenReturn(mPackageInstaller);
100         when(mPackageInstaller.createSession(any(PackageInstaller.SessionParams.class))).thenAnswer(
101                 (Answer<Integer>) invocation -> sSessionId++);
102         when(mPackageInstaller.openSession(anyInt())).thenReturn(mSession);
103         when(mSession.openWrite(anyString(), anyLong(), anyLong())).thenReturn(mSessionWriteStream);
104         when(mMockContext.registerReceiver(any(BroadcastReceiver.class),
105                 any(IntentFilter.class))).thenAnswer(
106                 (Answer<Intent>) invocation -> (Intent) getContext().registerReceiver(
107                         invocation.getArgument(0), invocation.getArgument(1)));
108         when(mMockContext.getSystemServiceName(eq(DevicePolicyManager.class)))
109                 .thenReturn(Context.DEVICE_POLICY_SERVICE);
110         when(mMockContext.getSystemService(eq(Context.DEVICE_POLICY_SERVICE))).thenReturn(mDpm);
111         when(mMockContext.getUser()).thenReturn(Process.myUserHandle());
112         when(mMockContext.getUserId()).thenReturn(UserHandle.myUserId());
113 
114         mTestPackageLocation = File.createTempFile("test", "apk").getPath();
115         try (FileOutputStream out = new FileOutputStream(mTestPackageLocation)) {
116             out.write(APK_CONTENT);
117         }
118 
119         mTask = new InstallPackageTask(mDownloadPackageTask, mMockContext, TEST_PARAMS, mCallback,
120                 mock(ProvisioningAnalyticsTracker.class));
121     }
122 
123     @SmallTest
testNoDownloadLocation()124     public void testNoDownloadLocation() {
125         // GIVEN no package was downloaded
126         when(mDownloadPackageTask.getDownloadedPackageLocation()).thenReturn(null);
127 
128         // WHEN running the InstallPackageTask without specifying an install location
129         mTask.run(TEST_USER_ID);
130         // THEN no package is installed, but we get a success callback
131         verify(mPackageManager, never()).getPackageInstaller();
132         verify(mCallback).onSuccess(mTask);
133         verifyNoMoreInteractions(mCallback);
134     }
135 
136     @SmallTest
testSuccess()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(
146                 INSTALL_REPLACE_EXISTING | INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS);
147 
148         // WHEN the package installed callback is invoked with success
149         Intent fillIn = new Intent();
150         fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, TEST_PACKAGE_NAME);
151         fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_SUCCESS);
152         observer.sendIntent(getContext(), 0, fillIn, null, null);
153 
154         // THEN we receive a success callback
155         verify(mCallback, timeout(TIMEOUT)).onSuccess(mTask);
156         verifyNoMoreInteractions(mCallback);
157     }
158 
159     @SmallTest
testSuccess_allowTestOnly()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                         | INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS);
173 
174         // WHEN the package installed callback is invoked with success
175         Intent fillIn = new Intent();
176         fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, TEST_PACKAGE_NAME);
177         fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_SUCCESS);
178         observer.sendIntent(getContext(), 0, fillIn, null, null);
179 
180         // THEN we receive a success callback
181         verify(mCallback, timeout(TIMEOUT)).onSuccess(mTask);
182         verifyNoMoreInteractions(mCallback);
183     }
184 
185 
186     @SmallTest
testInstallFailedVersionDowngrade()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(
196                 INSTALL_REPLACE_EXISTING | INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS);
197 
198         // WHEN the package installed callback is invoked with version downgrade error
199         Intent fillIn = new Intent();
200         fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, TEST_PACKAGE_NAME);
201         fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
202         fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS,
203                 PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE);
204         observer.sendIntent(getContext(), 0, fillIn, null, null);
205 
206         // THEN we get a success callback, because an existing version of the DPC is present
207         verify(mCallback, timeout(TIMEOUT)).onSuccess(mTask);
208         verifyNoMoreInteractions(mCallback);
209     }
210 
211     @SmallTest
testInstallFailedOtherError()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(
221                 INSTALL_REPLACE_EXISTING | INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS);
222 
223         // WHEN the package installed callback is invoked with version invalid apk error
224         Intent fillIn = new Intent();
225         fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, TEST_PACKAGE_NAME);
226         fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
227         fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS,
228                 PackageManager.INSTALL_FAILED_INVALID_APK);
229         observer.sendIntent(getContext(), 0, fillIn, null, null);
230 
231         // THEN we get a success callback, because an existing version of the DPC is present
232         verify(mCallback, timeout(TIMEOUT)).onError(mTask, ERROR_INSTALLATION_FAILED);
233         verifyNoMoreInteractions(mCallback);
234     }
235 
236     @SmallTest
testDifferentPackageName()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(
246                 INSTALL_REPLACE_EXISTING | INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS);
247 
248         // WHEN the package installed callback is invoked with the wrong package
249         Intent fillIn = new Intent();
250         fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, OTHER_PACKAGE_NAME);
251         observer.sendIntent(getContext(), 0, fillIn, null, null);
252 
253         // THEN we get a success callback, because the wrong package name
254         verify(mCallback, timeout(TIMEOUT)).onError(mTask, ERROR_PACKAGE_INVALID);
255         verifyNoMoreInteractions(mCallback);
256     }
257 
verifyPackageInstalled(int installFlags)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