1/*
2 * Copyright (C) 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.common;
18
19import static org.mockito.Matchers.contains;
20import static org.mockito.Mockito.any;
21import static org.mockito.Mockito.anyInt;
22import static org.mockito.Mockito.eq;
23import static org.mockito.Mockito.verify;
24import static org.mockito.Mockito.verifyNoMoreInteractions;
25import static org.mockito.Mockito.when;
26
27import android.accounts.AccountManager;
28import android.content.ComponentName;
29import android.content.Context;
30import android.content.Intent;
31import android.content.pm.ActivityInfo;
32import android.content.pm.ApplicationInfo;
33import android.content.pm.IPackageManager;
34import android.content.pm.PackageInfo;
35import android.content.pm.PackageManager;
36import android.content.pm.PackageManager.NameNotFoundException;
37import android.content.pm.ParceledListSlice;
38import android.content.pm.ResolveInfo;
39import android.graphics.Color;
40import android.net.ConnectivityManager;
41import android.net.NetworkInfo;
42import android.os.Build;
43import android.test.AndroidTestCase;
44import android.test.suitebuilder.annotation.SmallTest;
45
46import java.io.File;
47import java.io.FileOutputStream;
48import java.util.ArrayList;
49import java.util.Arrays;
50import java.util.List;
51import java.util.Set;
52
53import org.mockito.Mock;
54import org.mockito.MockitoAnnotations;
55
56/**
57 * Unit-tests for {@link Utils}.
58 */
59@SmallTest
60public class UtilsTest extends AndroidTestCase {
61    private static final String TEST_PACKAGE_NAME_1 = "com.test.packagea";
62    private static final String TEST_PACKAGE_NAME_2 = "com.test.packageb";
63    private static final String TEST_DEVICE_ADMIN_NAME = TEST_PACKAGE_NAME_1 + ".DeviceAdmin";
64    // Another DeviceAdmin in package 1
65    private static final String TEST_DEVICE_ADMIN_NAME_2 = TEST_PACKAGE_NAME_1 + ".DeviceAdmin2";
66    private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(TEST_PACKAGE_NAME_1,
67            TEST_DEVICE_ADMIN_NAME);
68    private static final ComponentName TEST_COMPONENT_NAME_2 = new ComponentName(TEST_PACKAGE_NAME_1,
69            TEST_DEVICE_ADMIN_NAME_2);
70    private static final int TEST_USER_ID = 10;
71    private static final String TEST_FILE_NAME = "testfile";
72
73    @Mock private Context mockContext;
74    @Mock private AccountManager mockAccountManager;
75    @Mock private IPackageManager mockIPackageManager;
76    @Mock private PackageManager mockPackageManager;
77    @Mock private ConnectivityManager mockConnectivityManager;
78
79    private Utils mUtils;
80
81    @Override
82    public void setUp() {
83        // this is necessary for mockito to work
84        System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
85
86        MockitoAnnotations.initMocks(this);
87
88        when(mockContext.getSystemService(Context.ACCOUNT_SERVICE)).thenReturn(mockAccountManager);
89        when(mockContext.getPackageManager()).thenReturn(mockPackageManager);
90        when(mockContext.getSystemService(Context.CONNECTIVITY_SERVICE))
91                .thenReturn(mockConnectivityManager);
92
93        mUtils = new Utils();
94    }
95
96    @Override
97    public void tearDown() {
98        mContext.deleteFile(TEST_FILE_NAME);
99    }
100
101    public void testGetCurrentSystemApps() throws Exception {
102        // GIVEN two currently installed apps, one of which is system
103        List<ApplicationInfo> appList = Arrays.asList(
104                createApplicationInfo(TEST_PACKAGE_NAME_1, false),
105                createApplicationInfo(TEST_PACKAGE_NAME_2, true));
106        when(mockIPackageManager.getInstalledApplications(
107                PackageManager.MATCH_UNINSTALLED_PACKAGES, TEST_USER_ID))
108                .thenReturn(new ParceledListSlice<ApplicationInfo>(appList));
109        // WHEN requesting the current system apps
110        Set<String> res = mUtils.getCurrentSystemApps(mockIPackageManager, TEST_USER_ID);
111        // THEN the one system app should be returned
112        assertEquals(1, res.size());
113        assertTrue(res.contains(TEST_PACKAGE_NAME_2));
114    }
115
116    public void testSetComponentEnabledSetting() throws Exception {
117        // GIVEN a component name and a user id
118        // WHEN disabling a component
119        mUtils.setComponentEnabledSetting(mockIPackageManager, TEST_COMPONENT_NAME,
120                PackageManager.COMPONENT_ENABLED_STATE_DISABLED, TEST_USER_ID);
121        // THEN the correct method on mockIPackageManager gets invoked
122        verify(mockIPackageManager).setComponentEnabledSetting(eq(TEST_COMPONENT_NAME),
123                eq(PackageManager.COMPONENT_ENABLED_STATE_DISABLED),
124                eq(PackageManager.DONT_KILL_APP),
125                eq(TEST_USER_ID));
126        verifyNoMoreInteractions(mockIPackageManager);
127    }
128
129    public void testPackageRequiresUpdate_notPresent() throws Exception {
130        // GIVEN that the requested package is not present on the device
131        // WHEN checking whether an update is required
132        when(mockPackageManager.getPackageInfo(TEST_PACKAGE_NAME_1, 0))
133                .thenThrow(new NameNotFoundException());
134        // THEN an update is required
135        assertTrue(mUtils.packageRequiresUpdate(TEST_PACKAGE_NAME_1, 0, mockContext));
136    }
137
138    public void testPackageRequiresUpdate() throws Exception {
139        // GIVEN a package that is installed on the device
140        PackageInfo pi = new PackageInfo();
141        pi.packageName = TEST_PACKAGE_NAME_1;
142        pi.versionCode = 1;
143        when(mockPackageManager.getPackageInfo(TEST_PACKAGE_NAME_1, 0)).thenReturn(pi);
144        // WHEN checking whether an update is required
145        // THEN verify that update required returns the correct result depending on the minimum
146        // version code requested.
147        assertFalse(mUtils.packageRequiresUpdate(TEST_PACKAGE_NAME_1, 0, mockContext));
148        assertFalse(mUtils.packageRequiresUpdate(TEST_PACKAGE_NAME_1, 1, mockContext));
149        assertTrue(mUtils.packageRequiresUpdate(TEST_PACKAGE_NAME_1, 2, mockContext));
150    }
151
152    public void testIsConnectedToNetwork() throws Exception {
153        // GIVEN the device is currently connected to mobile network
154        setCurrentNetworkMock(ConnectivityManager.TYPE_MOBILE, true);
155        // WHEN checking connectivity
156        // THEN utils should return true
157        assertTrue(mUtils.isConnectedToNetwork(mockContext));
158
159        // GIVEN the device is currently connected to wifi
160        setCurrentNetworkMock(ConnectivityManager.TYPE_WIFI, true);
161        // WHEN checking connectivity
162        // THEN utils should return true
163        assertTrue(mUtils.isConnectedToNetwork(mockContext));
164
165        // GIVEN the device is currently disconnected on wifi
166        setCurrentNetworkMock(ConnectivityManager.TYPE_WIFI, false);
167        // WHEN checking connectivity
168        // THEN utils should return false
169        assertFalse(mUtils.isConnectedToNetwork(mockContext));
170    }
171
172    public void testIsConnectedToWifi() throws Exception {
173        // GIVEN the device is currently connected to mobile network
174        setCurrentNetworkMock(ConnectivityManager.TYPE_MOBILE, true);
175        // WHEN checking whether connected to wifi
176        // THEN utils should return false
177        assertFalse(mUtils.isConnectedToWifi(mockContext));
178
179        // GIVEN the device is currently connected to wifi
180        setCurrentNetworkMock(ConnectivityManager.TYPE_WIFI, true);
181        // WHEN checking whether connected to wifi
182        // THEN utils should return true
183        assertTrue(mUtils.isConnectedToWifi(mockContext));
184
185        // GIVEN the device is currently disconnected on wifi
186        setCurrentNetworkMock(ConnectivityManager.TYPE_WIFI, false);
187        // WHEN checking whether connected to wifi
188        // THEN utils should return false
189        assertFalse(mUtils.isConnectedToWifi(mockContext));
190    }
191
192    public void testGetActiveNetworkInfo() throws Exception {
193        // GIVEN the device is connected to a network.
194        final NetworkInfo networkInfo =
195                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, null, null);
196        when(mockConnectivityManager.getActiveNetworkInfo()).thenReturn(networkInfo);
197        // THEN calling getActiveNetworkInfo should return the correct network info.
198        assertEquals(mUtils.getActiveNetworkInfo(mockContext), networkInfo);
199    }
200
201    public void testCurrentLauncherSupportsManagedProfiles_noLauncherSet() throws Exception {
202        // GIVEN there currently is no default launcher set
203        when(mockPackageManager.resolveActivity(any(Intent.class), anyInt()))
204                .thenReturn(null);
205        // WHEN checking whether the current launcher support managed profiles
206        // THEN utils should return false
207        assertFalse(mUtils.currentLauncherSupportsManagedProfiles(mockContext));
208    }
209
210    public void testCurrentLauncherSupportsManagedProfiles() throws Exception {
211        // GIVEN the current default launcher is built against lollipop
212        setLauncherMock(Build.VERSION_CODES.LOLLIPOP);
213        // WHEN checking whether the current launcher support managed profiles
214        // THEN utils should return true
215        assertTrue(mUtils.currentLauncherSupportsManagedProfiles(mockContext));
216
217        // GIVEN the current default launcher is built against kitkat
218        setLauncherMock(Build.VERSION_CODES.KITKAT);
219        // WHEN checking whether the current launcher support managed profiles
220        // THEN utils should return false
221        assertFalse(mUtils.currentLauncherSupportsManagedProfiles(mockContext));
222    }
223
224    public void testFindDeviceAdmin_ComponentName() throws Exception {
225        // GIVEN a package info with more than one device admin
226        setUpPackage(TEST_PACKAGE_NAME_1, TEST_DEVICE_ADMIN_NAME, TEST_DEVICE_ADMIN_NAME_2);
227
228        // THEN calling findDeviceAdmin returns the correct admin
229        assertEquals(TEST_COMPONENT_NAME_2,
230                mUtils.findDeviceAdmin(null, TEST_COMPONENT_NAME_2, mockContext));
231    }
232
233    public void testFindDeviceAdmin_PackageName() throws Exception {
234        // GIVEN a package info with one device admin
235        setUpPackage(TEST_PACKAGE_NAME_1, TEST_DEVICE_ADMIN_NAME);
236
237        // THEN calling findDeviceAdmin returns the correct admin
238        assertEquals(TEST_COMPONENT_NAME,
239                mUtils.findDeviceAdmin(TEST_PACKAGE_NAME_1, null, mockContext));
240    }
241
242    public void testFindDeviceAdmin_NoPackageName() throws Exception {
243        // GIVEN no package info file
244        when(mockPackageManager.getPackageInfo(TEST_PACKAGE_NAME_1,
245                PackageManager.GET_RECEIVERS | PackageManager.MATCH_DISABLED_COMPONENTS))
246                .thenReturn(null);
247
248        // THEN throw IllegalProvisioningArgumentException
249        try {
250            mUtils.findDeviceAdmin(TEST_PACKAGE_NAME_1, null, mockContext);
251            fail();
252        } catch (IllegalProvisioningArgumentException e) {
253            // expected
254        }
255    }
256
257    public void testFindDeviceAdmin_AnotherComponentName() throws Exception {
258        // GIVEN a package info with one device admin
259        setUpPackage(TEST_PACKAGE_NAME_1, TEST_DEVICE_ADMIN_NAME);
260
261        // THEN looking another device admin throws IllegalProvisioningArgumentException
262        try {
263            mUtils.findDeviceAdmin(null, TEST_COMPONENT_NAME_2, mockContext);
264            fail();
265        } catch (IllegalProvisioningArgumentException e) {
266            // expected
267        }
268    }
269
270    public void testFindDeviceAdminInPackageInfo_Success() throws Exception {
271        // GIVEN a package info with one device admin
272        PackageInfo packageInfo = setUpPackage(TEST_PACKAGE_NAME_1, TEST_DEVICE_ADMIN_NAME);
273
274        // THEN calling findDeviceAdminInPackageInfo returns the correct admin
275        assertEquals(TEST_COMPONENT_NAME,
276                mUtils.findDeviceAdminInPackageInfo(TEST_PACKAGE_NAME_1, null, packageInfo));
277    }
278
279    public void testFindDeviceAdminInPackageInfo_PackageNameMismatch() throws Exception {
280        // GIVEN a package info with one device admin
281        PackageInfo packageInfo = setUpPackage(TEST_PACKAGE_NAME_1, TEST_DEVICE_ADMIN_NAME);
282
283        // THEN calling findDeviceAdminInPackageInfo with the wrong package name return null
284        assertNull(mUtils.findDeviceAdminInPackageInfo(TEST_PACKAGE_NAME_2, null, packageInfo));
285    }
286
287    public void testFindDeviceAdminInPackageInfo_NoAdmin() throws Exception {
288        // GIVEN a package info with no device admin
289        PackageInfo packageInfo = setUpPackage(TEST_PACKAGE_NAME_1);
290
291        // THEN calling findDeviceAdminInPackageInfo returns null
292        assertNull(mUtils.findDeviceAdminInPackageInfo(TEST_PACKAGE_NAME_1, null, packageInfo));
293    }
294
295    public void testFindDeviceAdminInPackageInfo_TwoAdmins() throws Exception {
296        // GIVEN a package info with more than one device admin
297        PackageInfo packageInfo = setUpPackage(TEST_PACKAGE_NAME_1, TEST_DEVICE_ADMIN_NAME,
298                TEST_DEVICE_ADMIN_NAME_2);
299
300        // THEN calling findDeviceAdminInPackageInfo returns null
301        assertNull(mUtils.findDeviceAdminInPackageInfo(TEST_PACKAGE_NAME_1, null, packageInfo));
302    }
303
304    public void testFindDeviceAdminInPackageInfo_TwoAdminsWithComponentName() throws Exception {
305        // GIVEN a package info with more than one device admin
306        PackageInfo packageInfo = setUpPackage(TEST_PACKAGE_NAME_1, TEST_DEVICE_ADMIN_NAME,
307                TEST_DEVICE_ADMIN_NAME_2);
308
309        // THEN calling findDeviceAdminInPackageInfo return component 1
310        assertEquals(TEST_COMPONENT_NAME, mUtils.findDeviceAdminInPackageInfo(
311                TEST_PACKAGE_NAME_1, TEST_COMPONENT_NAME, packageInfo));
312    }
313
314
315    public void testFindDeviceAdminInPackageInfo_InvalidComponentName() throws Exception {
316        // GIVEN a package info with component 1
317        PackageInfo packageInfo = setUpPackage(TEST_PACKAGE_NAME_1, TEST_DEVICE_ADMIN_NAME);
318
319        // THEN calling findDeviceAdminInPackageInfo with component 2 returns null
320        assertNull(mUtils.findDeviceAdminInPackageInfo(
321                TEST_PACKAGE_NAME_1, TEST_COMPONENT_NAME_2, packageInfo));
322    }
323
324    public void testComputeHashOfByteArray() {
325        // GIVEN a byte array
326        byte[] bytes = "TESTARRAY".getBytes();
327        // GIVEN its Sha256 hash
328        byte[] sha256 = new byte[] {100, -45, -118, -68, -104, -15, 63, -60, -84, -44, -13, -63,
329                53, -50, 104, -63, 38, 122, 16, -44, -85, -50, 67, 98, 78, 121, 11, 72, 79, 40, 107,
330                125};
331
332        // THEN computeHashOfByteArray returns the correct result
333        assertTrue(Arrays.equals(sha256, mUtils.computeHashOfByteArray(bytes)));
334    }
335
336    public void testComputeHashOfFile() {
337        // GIVEN a file with test data
338        final String fileLocation = getContext().getFilesDir().toString() + "/" + TEST_FILE_NAME;
339        String string = "Hello world!";
340        FileOutputStream outputStream;
341        try {
342            outputStream = getContext().openFileOutput(TEST_FILE_NAME, Context.MODE_PRIVATE);
343            outputStream.write(string.getBytes());
344            outputStream.close();
345        } catch (Exception e) {
346            e.printStackTrace();
347        }
348        // GIVEN the file's Sha256 hash
349        byte[] sha256 = new byte[] {-64, 83, 94, 75, -30, -73, -97, -3, -109, 41, 19, 5, 67, 107,
350                -8, -119, 49, 78, 74, 63, -82, -64, 94, -49, -4, -69, 125, -13, 26, -39, -27, 26};
351        // GIVEN the file's Sha1 hash
352        byte[] sha1 = new byte[] {-45, 72, 106, -23, 19, 110, 120, 86, -68, 66, 33, 35, -123, -22,
353                121, 112, -108, 71, 88, 2};
354
355        //THEN the Sha256 hash is correct
356        assertTrue(
357                Arrays.equals(sha256, mUtils.computeHashOfFile(fileLocation, Utils.SHA256_TYPE)));
358        //THEN the Sha1 hash is correct
359        assertTrue(Arrays.equals(sha1, mUtils.computeHashOfFile(fileLocation, Utils.SHA1_TYPE)));
360    }
361
362    public void testComputeHashOfFile_NotPresent() {
363        // GIVEN no file is present
364        final String fileLocation = getContext().getFilesDir().toString() + "/" + TEST_FILE_NAME;
365        getContext().deleteFile(TEST_FILE_NAME);
366
367        // THEN computeHashOfFile should return null
368        assertNull(mUtils.computeHashOfFile(fileLocation, Utils.SHA256_TYPE));
369        assertNull(mUtils.computeHashOfFile(fileLocation, Utils.SHA1_TYPE));
370    }
371
372    public void testBrightColors() {
373        assertTrue(mUtils.isBrightColor(Color.WHITE));
374        assertTrue(mUtils.isBrightColor(Color.YELLOW));
375        assertFalse(mUtils.isBrightColor(Color.BLACK));
376        assertFalse(mUtils.isBrightColor(Color.BLUE));
377    }
378
379    public void testCanResolveIntentAsUser() {
380        // GIVEN intent is null
381        // THEN intent should not be resolved
382        assertFalse(mUtils.canResolveIntentAsUser(mockContext, null, TEST_USER_ID));
383
384        // GIVEN a valid intent
385        Intent intent = new Intent();
386
387        // WHEN resolve activity as user returns null
388        when(mockPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), anyInt()))
389                .thenReturn(null);
390        // THEN intent should not be resolved for user
391        assertFalse(mUtils.canResolveIntentAsUser(mockContext, intent, TEST_USER_ID));
392
393        // WHEN resolve activity as user returns valid resolve info
394        when(mockPackageManager.resolveActivityAsUser(any(Intent.class), anyInt(), anyInt()))
395                .thenReturn(new ResolveInfo());
396        // THEN intent should be resolved
397        assertTrue(mUtils.canResolveIntentAsUser(mockContext, intent, TEST_USER_ID));
398    }
399
400    private ApplicationInfo createApplicationInfo(String packageName, boolean system) {
401        ApplicationInfo ai = new ApplicationInfo();
402        ai.packageName = packageName;
403        if (system) {
404            ai.flags = ApplicationInfo.FLAG_SYSTEM;
405        }
406        return ai;
407    }
408
409    private void setCurrentNetworkMock(int type, boolean connected) {
410        NetworkInfo networkInfo = new NetworkInfo(type, 0, null, null);
411        networkInfo.setDetailedState(
412                connected ? NetworkInfo.DetailedState.CONNECTED
413                        : NetworkInfo.DetailedState.DISCONNECTED,
414                null, null);
415        when(mockConnectivityManager.getActiveNetworkInfo()).thenReturn(networkInfo);
416    }
417
418    private void setLauncherMock(int targetSdkVersion) throws Exception {
419        ApplicationInfo appInfo = new ApplicationInfo();
420        appInfo.targetSdkVersion = targetSdkVersion;
421        ActivityInfo actInfo = new ActivityInfo();
422        actInfo.packageName = TEST_PACKAGE_NAME_1;
423        ResolveInfo resInfo = new ResolveInfo();
424        resInfo.activityInfo = actInfo;
425
426        when(mockPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(resInfo);
427        when(mockPackageManager.getApplicationInfo(TEST_PACKAGE_NAME_1, 0)).thenReturn(appInfo);
428    }
429
430    private PackageInfo setUpPackage(String packageName, String... adminNames)
431            throws NameNotFoundException {
432        PackageInfo packageInfo = new PackageInfo();
433        packageInfo.packageName = packageName;
434        packageInfo.receivers = new ActivityInfo[adminNames.length];
435        for (int i = 0; i < adminNames.length; i++) {
436            ActivityInfo receiver = new ActivityInfo();
437            receiver.permission = android.Manifest.permission.BIND_DEVICE_ADMIN;
438            receiver.name = adminNames[i];
439            packageInfo.receivers[i] = receiver;
440        }
441        when(mockPackageManager.getPackageInfo(packageName,
442                PackageManager.GET_RECEIVERS | PackageManager.MATCH_DISABLED_COMPONENTS))
443                .thenReturn(packageInfo);
444
445        return packageInfo;
446    }
447}
448