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