• 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_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.mock;
32 import static org.mockito.Mockito.never;
33 import static org.mockito.Mockito.timeout;
34 import static org.mockito.Mockito.verify;
35 import static org.mockito.Mockito.verifyNoMoreInteractions;
36 import static org.mockito.Mockito.when;
37 
38 import android.app.admin.DevicePolicyManager;
39 import android.content.BroadcastReceiver;
40 import android.content.Context;
41 import android.content.Intent;
42 import android.content.IntentFilter;
43 import android.content.IntentSender;
44 import android.content.pm.PackageInstaller;
45 import android.content.pm.PackageManager;
46 import android.os.Process;
47 import android.os.UserHandle;
48 import android.test.AndroidTestCase;
49 import android.test.suitebuilder.annotation.SmallTest;
50 
51 import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker;
52 import com.android.managedprovisioning.model.ProvisioningParams;
53 
54 import org.mockito.ArgumentCaptor;
55 import org.mockito.Mock;
56 import org.mockito.MockitoAnnotations;
57 import org.mockito.stubbing.Answer;
58 
59 import java.io.File;
60 import java.io.FileOutputStream;
61 import java.io.IOException;
62 import java.io.OutputStream;
63 import java.util.Arrays;
64 
65 public class InstallPackageTaskTest extends AndroidTestCase {
66     private static final String TEST_PACKAGE_NAME = "com.android.test";
67     private static final String OTHER_PACKAGE_NAME = "com.android.other";
68     private static final ProvisioningParams TEST_PARAMS = new ProvisioningParams.Builder()
69             .setDeviceAdminPackageName(TEST_PACKAGE_NAME)
70             .setProvisioningAction(ACTION_PROVISION_MANAGED_DEVICE)
71             .build();
72     private static final int TEST_USER_ID = 123;
73     private static final byte[] APK_CONTENT = new byte[]{'t', 'e', 's', 't'};
74     private static final long TIMEOUT = 10000;
75 
76     private static int sSessionId = 0;
77 
78     @Mock private Context mMockContext;
79     @Mock private PackageManager mPackageManager;
80     @Mock private PackageInstaller mPackageInstaller;
81     @Mock private PackageInstaller.Session mSession;
82     @Mock private OutputStream mSessionWriteStream;
83     @Mock private DevicePolicyManager mDpm;
84     @Mock private AbstractProvisioningTask.Callback mCallback;
85     @Mock private DownloadPackageTask mDownloadPackageTask;
86     private InstallPackageTask mTask;
87     private String mTestPackageLocation;
88 
89     @Override
setUp()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(mDownloadPackageTask, mMockContext, TEST_PARAMS, mCallback,
119                 mock(ProvisioningAnalyticsTracker.class));
120     }
121 
122     @SmallTest
testNoDownloadLocation()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     }
134 
135     @SmallTest
testSuccess()136     public void testSuccess() throws Exception {
137         // GIVEN a package was downloaded to TEST_LOCATION
138         when(mDownloadPackageTask.getDownloadedPackageLocation()).thenReturn(mTestPackageLocation);
139 
140         // WHEN running the InstallPackageTask specifying an install location
141         mTask.run(TEST_USER_ID);
142 
143         // THEN package installed is invoked with an install observer
144         IntentSender observer = verifyPackageInstalled(INSTALL_REPLACE_EXISTING);
145 
146         // WHEN the package installed callback is invoked with success
147         Intent fillIn = new Intent();
148         fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, TEST_PACKAGE_NAME);
149         fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_SUCCESS);
150         observer.sendIntent(getContext(), 0, fillIn, null, null);
151 
152         // THEN we receive a success callback
153         verify(mCallback, timeout(TIMEOUT)).onSuccess(mTask);
154         verifyNoMoreInteractions(mCallback);
155     }
156 
157     @SmallTest
testSuccess_allowTestOnly()158     public void testSuccess_allowTestOnly() throws Exception {
159         // GIVEN a package was downloaded to TEST_LOCATION
160         when(mDownloadPackageTask.getDownloadedPackageLocation()).thenReturn(mTestPackageLocation);
161         // WHEN package to be installed is the current device owner.
162         when(mDpm.isDeviceOwnerApp(eq(TEST_PACKAGE_NAME))).thenReturn(true);
163 
164         // WHEN running the InstallPackageTask specifying an install location
165         mTask.run(TEST_USER_ID);
166 
167         // THEN package installed is invoked with an install observer
168         IntentSender observer = verifyPackageInstalled(
169                 INSTALL_REPLACE_EXISTING | INSTALL_ALLOW_TEST);
170 
171         // WHEN the package installed callback is invoked with success
172         Intent fillIn = new Intent();
173         fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, TEST_PACKAGE_NAME);
174         fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_SUCCESS);
175         observer.sendIntent(getContext(), 0, fillIn, null, null);
176 
177         // THEN we receive a success callback
178         verify(mCallback, timeout(TIMEOUT)).onSuccess(mTask);
179         verifyNoMoreInteractions(mCallback);
180     }
181 
182 
183     @SmallTest
testInstallFailedVersionDowngrade()184     public void testInstallFailedVersionDowngrade() throws Exception {
185         // GIVEN a package was downloaded to TEST_LOCATION
186         when(mDownloadPackageTask.getDownloadedPackageLocation()).thenReturn(mTestPackageLocation);
187 
188         // WHEN running the InstallPackageTask with a package already at a higher version
189         mTask.run(TEST_USER_ID);
190 
191         // THEN package installed is invoked with an install observer
192         IntentSender observer = verifyPackageInstalled(INSTALL_REPLACE_EXISTING);
193 
194         // WHEN the package installed callback is invoked with version downgrade error
195         Intent fillIn = new Intent();
196         fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, TEST_PACKAGE_NAME);
197         fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
198         fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS,
199                 PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE);
200         observer.sendIntent(getContext(), 0, fillIn, null, null);
201 
202         // THEN we get a success callback, because an existing version of the DPC is present
203         verify(mCallback, timeout(TIMEOUT)).onSuccess(mTask);
204         verifyNoMoreInteractions(mCallback);
205     }
206 
207     @SmallTest
testInstallFailedOtherError()208     public void testInstallFailedOtherError() throws Exception {
209         // GIVEN a package was downloaded to TEST_LOCATION
210         when(mDownloadPackageTask.getDownloadedPackageLocation()).thenReturn(mTestPackageLocation);
211 
212         // WHEN running the InstallPackageTask with a package already at a higher version
213         mTask.run(TEST_USER_ID);
214 
215         // THEN package installed is invoked with an install observer
216         IntentSender observer = verifyPackageInstalled(INSTALL_REPLACE_EXISTING);
217 
218         // WHEN the package installed callback is invoked with version invalid apk error
219         Intent fillIn = new Intent();
220         fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, TEST_PACKAGE_NAME);
221         fillIn.putExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
222         fillIn.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS,
223                 PackageManager.INSTALL_FAILED_INVALID_APK);
224         observer.sendIntent(getContext(), 0, fillIn, null, null);
225 
226         // THEN we get a success callback, because an existing version of the DPC is present
227         verify(mCallback, timeout(TIMEOUT)).onError(mTask, ERROR_INSTALLATION_FAILED);
228         verifyNoMoreInteractions(mCallback);
229     }
230 
231     @SmallTest
testDifferentPackageName()232     public void testDifferentPackageName() throws Exception {
233         // GIVEN a package was downloaded to TEST_LOCATION
234         when(mDownloadPackageTask.getDownloadedPackageLocation()).thenReturn(mTestPackageLocation);
235 
236         // WHEN running the InstallPackageTask with a package already at a higher version
237         mTask.run(TEST_USER_ID);
238 
239         // THEN package installed is invoked with an install observer
240         IntentSender observer = verifyPackageInstalled(INSTALL_REPLACE_EXISTING);
241 
242         // WHEN the package installed callback is invoked with the wrong package
243         Intent fillIn = new Intent();
244         fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, OTHER_PACKAGE_NAME);
245         observer.sendIntent(getContext(), 0, fillIn, null, null);
246 
247         // THEN we get a success callback, because the wrong package name
248         verify(mCallback, timeout(TIMEOUT)).onError(mTask, ERROR_PACKAGE_INVALID);
249         verifyNoMoreInteractions(mCallback);
250     }
251 
verifyPackageInstalled(int installFlags)252     private IntentSender verifyPackageInstalled(int installFlags) throws IOException {
253         ArgumentCaptor<PackageInstaller.SessionParams> paramsCaptor
254                 = ArgumentCaptor.forClass(PackageInstaller.SessionParams.class);
255         ArgumentCaptor<byte[]> fileContentCaptor = ArgumentCaptor.forClass(byte[].class);
256 
257         verify(mPackageInstaller).createSession(paramsCaptor.capture());
258         assertEquals(installFlags, paramsCaptor.getValue().installFlags);
259         verify(mSessionWriteStream).write(fileContentCaptor.capture(), eq(0),
260                 eq(APK_CONTENT.length));
261         assertTrue(Arrays.equals(APK_CONTENT,
262                 Arrays.copyOf(fileContentCaptor.getValue(), APK_CONTENT.length)));
263 
264         ArgumentCaptor<IntentSender> intentSenderCaptor
265                 = ArgumentCaptor.forClass(IntentSender.class);
266 
267         // THEN package installation was started and we will receive a status callback
268         verify(mSession).commit(intentSenderCaptor.capture());
269         return intentSenderCaptor.getValue();
270     }
271 }
272