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