1 /*
2  * Copyright 2016, 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 
21 import static com.android.managedprovisioning.task.VerifyPackageTask.ERROR_DEVICE_ADMIN_MISSING;
22 import static com.android.managedprovisioning.task.VerifyPackageTask.ERROR_HASH_MISMATCH;
23 
24 import static org.mockito.Matchers.any;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.verifyNoMoreInteractions;
28 import static org.mockito.Mockito.when;
29 
30 import android.content.ComponentName;
31 import android.content.Context;
32 import android.content.pm.PackageInfo;
33 import android.content.pm.PackageManager;
34 import android.content.pm.Signature;
35 
36 import androidx.test.filters.SmallTest;
37 import androidx.test.runner.AndroidJUnit4;
38 
39 import com.android.managedprovisioning.analytics.ProvisioningAnalyticsTracker;
40 import com.android.managedprovisioning.common.Utils;
41 import com.android.managedprovisioning.model.PackageDownloadInfo;
42 import com.android.managedprovisioning.model.ProvisioningParams;
43 
44 import org.junit.Before;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 import org.mockito.Mock;
48 import org.mockito.MockitoAnnotations;
49 
50 import java.io.File;
51 
52 /**
53  * Unit tests for {@link VerifyPackageTask}.
54  */
55 @RunWith(AndroidJUnit4.class)
56 @SmallTest
57 public class VerifyPackageTaskTest {
58 
59     private static final String TEST_PACKAGE_NAME = "sample.package.name";
60     private static final String TEST_ADMIN_NAME = TEST_PACKAGE_NAME + ".DeviceAdmin";
61     private static final String TEST_PACKAGE_LOCATION = "http://www.some.uri.com";
62     private static final String TEST_LOCAL_FILENAME = "/local/filename";
63     private static final File TEST_LOCAL_FILE = new File(TEST_LOCAL_FILENAME);
64     private static final int TEST_USER_ID = 123;
65     private static final byte[] TEST_BAD_HASH = new byte[] { 'b', 'a', 'd' };
66     private static final byte[] TEST_PACKAGE_CHECKSUM_HASH = new byte[] { '1', '2', '3', '4', '5' };
67     private static final byte[] TEST_SIGNATURE_HASH = new byte[] {'a', 'b', 'c', 'd'};
68     private static final byte[] EMPTY_BYTE_ARRAY = new byte[] {};
69     private static final Signature[] TEST_SIGNATURES = new Signature[] { new Signature("1986") };
70 
71     @Mock private Context mContext;
72     @Mock private DownloadPackageTask mDownloadPackageTask;
73     @Mock private AbstractProvisioningTask.Callback mCallback;
74     @Mock private PackageManager mPackageManager;
75     @Mock private Utils mUtils;
76     @Mock private PackageInfo mPackageInfo;
77 
78     private AbstractProvisioningTask mTask;
79 
80     @Before
setUp()81     public void setUp() throws Exception {
82         // This is necessary for mockito to work
83         MockitoAnnotations.initMocks(this);
84 
85         when(mContext.getPackageManager()).thenReturn(mPackageManager);
86 
87         mPackageInfo.packageName = TEST_PACKAGE_NAME;
88         mPackageInfo.signatures = TEST_SIGNATURES;
89 
90         when(mPackageManager.getPackageArchiveInfo(TEST_LOCAL_FILENAME,
91                 PackageManager.GET_SIGNATURES | PackageManager.GET_RECEIVERS))
92                 .thenReturn(mPackageInfo);
93 
94         when(mDownloadPackageTask.getPackageLocation()).thenReturn(TEST_LOCAL_FILE);
95 
96         when(mUtils.findDeviceAdminInPackageInfo(TEST_PACKAGE_NAME, null, mPackageInfo))
97                 .thenReturn(new ComponentName(TEST_PACKAGE_NAME, TEST_ADMIN_NAME));
98     }
99 
100     @Test
testDownloadLocationNull()101     public void testDownloadLocationNull() {
102         // GIVEN that the download package location is null
103         when(mDownloadPackageTask.getPackageLocation()).thenReturn(null);
104 
105         // WHEN running the VerifyPackageTask
106         runWithDownloadInfo(TEST_PACKAGE_CHECKSUM_HASH, EMPTY_BYTE_ARRAY);
107 
108         // THEN success should be called
109         verify(mCallback).onSuccess(mTask);
110         verifyNoMoreInteractions(mCallback);
111     }
112 
113     @Test
testMissingDeviceAdminComponent()114     public void testMissingDeviceAdminComponent() {
115         // GIVEN that the device admin component cannot be found
116         when(mUtils.findDeviceAdminInPackageInfo(TEST_PACKAGE_NAME, null, mPackageInfo))
117                 .thenReturn(null);
118 
119         // WHEN running the VerifyPackageTask
120         runWithDownloadInfo(TEST_PACKAGE_CHECKSUM_HASH, EMPTY_BYTE_ARRAY);
121 
122         // THEN an error should be reported
123         verify(mCallback).onError(mTask, ERROR_DEVICE_ADMIN_MISSING);
124         verifyNoMoreInteractions(mCallback);
125     }
126 
127     @Test
testPackageChecksumSha256_success()128     public void testPackageChecksumSha256_success() throws Exception {
129         // GIVEN the hash of the downloaded file matches the parameter value
130         when(mUtils.computeHashOfFile(TEST_LOCAL_FILENAME, Utils.SHA256_TYPE))
131                 .thenReturn(TEST_PACKAGE_CHECKSUM_HASH);
132 
133         // WHEN running the VerifyPackageTask
134         runWithDownloadInfo(TEST_PACKAGE_CHECKSUM_HASH, EMPTY_BYTE_ARRAY);
135 
136         // THEN success should be called
137         verify(mCallback).onSuccess(mTask);
138         verifyNoMoreInteractions(mCallback);
139     }
140 
141     @Test
testSignatureHash_success()142     public void testSignatureHash_success() throws Exception {
143         // GIVEN the hash of the signature matches the parameter value
144         when(mUtils.computeHashOfByteArray(TEST_SIGNATURES[0].toByteArray()))
145                 .thenReturn(TEST_SIGNATURE_HASH);
146 
147         // WHEN running the VerifyPackageTask
148         runWithDownloadInfo(EMPTY_BYTE_ARRAY, TEST_SIGNATURE_HASH);
149 
150         // THEN success should be called
151         verify(mCallback).onSuccess(mTask);
152         verifyNoMoreInteractions(mCallback);
153     }
154 
155     @Test
testSignatureHash_failure()156     public void testSignatureHash_failure() throws Exception {
157         // GIVEN the hash of the signature does not match the parameter value
158         when(mUtils.computeHashOfByteArray(TEST_SIGNATURES[0].toByteArray()))
159                 .thenReturn(TEST_BAD_HASH);
160 
161         // WHEN running the VerifyPackageTask
162         runWithDownloadInfo(EMPTY_BYTE_ARRAY, TEST_SIGNATURE_HASH);
163 
164         // THEN hash mismatch error should be called
165         verify(mCallback).onError(mTask, ERROR_HASH_MISMATCH);
166         verifyNoMoreInteractions(mCallback);
167     }
168 
169     @Test
testSignatureHash_noSignature()170     public void testSignatureHash_noSignature() throws Exception {
171         // GIVEN the package has no signature
172         mPackageInfo.signatures = null;
173 
174         // WHEN running the VerifyPackageTask
175         runWithDownloadInfo(EMPTY_BYTE_ARRAY, TEST_SIGNATURE_HASH);
176 
177         // THEN hash mismatch error should be called
178         verify(mCallback).onError(mTask, ERROR_HASH_MISMATCH);
179         verifyNoMoreInteractions(mCallback);
180     }
181 
182     @Test
testSignatureHash_digestFailure()183     public void testSignatureHash_digestFailure() throws Exception {
184         // GIVEN the package has no signature
185         when(mUtils.computeHashOfByteArray(any(byte[].class))).thenReturn(null);
186 
187         // WHEN running the VerifyPackageTask
188         runWithDownloadInfo(EMPTY_BYTE_ARRAY, TEST_SIGNATURE_HASH);
189 
190         // THEN hash mismatch error should be called
191         verify(mCallback).onError(mTask, ERROR_HASH_MISMATCH);
192         verifyNoMoreInteractions(mCallback);
193     }
194 
runWithDownloadInfo(byte[] packageChecksum, byte[] signatureChecksum)195     private void runWithDownloadInfo(byte[] packageChecksum, byte[] signatureChecksum) {
196         PackageDownloadInfo downloadInfo = new PackageDownloadInfo.Builder()
197                 .setLocation(TEST_PACKAGE_LOCATION)
198                 .setPackageChecksum(packageChecksum)
199                 .setSignatureChecksum(signatureChecksum)
200                 .build();
201         ProvisioningParams params = new ProvisioningParams.Builder()
202                 .setProvisioningAction(ACTION_PROVISION_MANAGED_DEVICE)
203                 .setDeviceAdminPackageName(TEST_PACKAGE_NAME)
204                 .setDeviceAdminDownloadInfo(downloadInfo)
205                 .build();
206         mTask = new VerifyPackageTask(mUtils, mDownloadPackageTask, mContext, params, mCallback,
207                 mock(ProvisioningAnalyticsTracker.class));
208         mTask.run(TEST_USER_ID);
209     }
210 }
211