1 /*
2  * Copyright (C) 2019 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.car.settings.security;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import android.app.admin.DevicePolicyManager;
22 import android.content.Context;
23 import android.content.ContextWrapper;
24 import android.content.Intent;
25 import android.os.RemoteException;
26 
27 import com.android.car.settings.CarSettingsRobolectricTestRunner;
28 import com.android.car.settings.setupservice.InitialLockSetupService;
29 import com.android.car.settings.testutils.ShadowLockPatternUtils;
30 import com.android.car.setupwizardlib.IInitialLockSetupService;
31 import com.android.car.setupwizardlib.InitialLockSetupConstants.LockTypes;
32 import com.android.car.setupwizardlib.InitialLockSetupConstants.SetLockCodes;
33 import com.android.car.setupwizardlib.InitialLockSetupConstants.ValidateLockFlags;
34 import com.android.car.setupwizardlib.InitialLockSetupHelper;
35 import com.android.car.setupwizardlib.LockConfig;
36 import com.android.internal.widget.LockPatternUtils;
37 import com.android.internal.widget.LockPatternView;
38 
39 import org.junit.Before;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 import org.robolectric.Robolectric;
43 import org.robolectric.RuntimeEnvironment;
44 import org.robolectric.Shadows;
45 import org.robolectric.annotation.Config;
46 import org.robolectric.shadows.ShadowContextWrapper;
47 
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.List;
51 
52 /**
53  * Tests that the {@link InitialLockSetupService} properly handles connections and lock requests.
54  */
55 @Config(shadows = ShadowLockPatternUtils.class)
56 @RunWith(CarSettingsRobolectricTestRunner.class)
57 public class InitialLockSetupServiceTest {
58 
59     private static final String LOCK_PERMISSION = "com.android.car.settings.SET_INITIAL_LOCK";
60 
61     private InitialLockSetupService mInitialLockSetupService;
62     private Context mContext;
63 
64     @Before
setupService()65     public void setupService() {
66         ShadowLockPatternUtils.reset();
67         mInitialLockSetupService = Robolectric.buildService(InitialLockSetupService.class)
68                 .create()
69                 .get();
70         mContext = RuntimeEnvironment.application;
71         ShadowContextWrapper shadowContextWrapper = Shadows.shadowOf((ContextWrapper) mContext);
72         shadowContextWrapper.grantPermissions(LOCK_PERMISSION);
73     }
74 
75     @Test
testBindReturnsNull_ifLockSet()76     public void testBindReturnsNull_ifLockSet() {
77         ShadowLockPatternUtils.setPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
78         assertThat(mInitialLockSetupService.onBind(new Intent())).isNull();
79     }
80 
81     @Test
testBindReturnsInstanceOfServiceInterface_ifLockNotSet()82     public void testBindReturnsInstanceOfServiceInterface_ifLockNotSet() throws RemoteException {
83         assertThat(mInitialLockSetupService.onBind(
84                 new Intent()) instanceof IInitialLockSetupService.Stub).isTrue();
85     }
86 
87     @Test
testGetLockConfig_returnsCorrectConfig()88     public void testGetLockConfig_returnsCorrectConfig() throws RemoteException {
89         IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
90                 mInitialLockSetupService.onBind(new Intent()));
91         LockConfig pinConfig = service.getLockConfig(LockTypes.PIN);
92         assertThat(pinConfig.enabled).isTrue();
93         assertThat(pinConfig.minLockLength).isEqualTo(LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
94         LockConfig patternConfig = service.getLockConfig(LockTypes.PATTERN);
95         assertThat(patternConfig.enabled).isTrue();
96         assertThat(patternConfig.minLockLength).isEqualTo(LockPatternUtils.MIN_LOCK_PATTERN_SIZE);
97         LockConfig passwordConfig = service.getLockConfig(LockTypes.PASSWORD);
98         assertThat(passwordConfig.enabled).isTrue();
99         assertThat(passwordConfig.minLockLength).isEqualTo(LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
100     }
101 
102     @Test
testCheckValidLock_tooShort()103     public void testCheckValidLock_tooShort() throws RemoteException {
104         IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
105                 mInitialLockSetupService.onBind(new Intent()));
106         int result = service.checkValidLock(LockTypes.PASSWORD, "hi".getBytes());
107         assertThat(result & ValidateLockFlags.INVALID_LENGTH)
108                 .isEqualTo(ValidateLockFlags.INVALID_LENGTH);
109     }
110 
111     @Test
testCheckValidLock_longEnough()112     public void testCheckValidLock_longEnough() throws RemoteException {
113         IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
114                 mInitialLockSetupService.onBind(new Intent()));
115         int result = service.checkValidLock(LockTypes.PASSWORD, "password".getBytes());
116         assertThat(result & ValidateLockFlags.INVALID_LENGTH)
117                 .isNotEqualTo(ValidateLockFlags.INVALID_LENGTH);
118     }
119 
120     @Test
testCheckValidLockPin_withLetters()121     public void testCheckValidLockPin_withLetters() throws RemoteException {
122         IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
123                 mInitialLockSetupService.onBind(new Intent()));
124         int result = service.checkValidLock(LockTypes.PIN, "12a3".getBytes());
125         assertThat(result & ValidateLockFlags.INVALID_BAD_SYMBOLS)
126                 .isEqualTo(ValidateLockFlags.INVALID_BAD_SYMBOLS);
127     }
128 
129     @Test
testCheckValidLockPattern_tooShort()130     public void testCheckValidLockPattern_tooShort() throws RemoteException {
131         IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
132                 mInitialLockSetupService.onBind(new Intent()));
133         byte[] pattern = new byte[LockPatternUtils.MIN_LOCK_PATTERN_SIZE - 1];
134         for (int i = 0; i < pattern.length; i++) {
135             pattern[i] = (byte) i;
136         }
137         int result = service.checkValidLock(LockTypes.PATTERN, pattern);
138         assertThat(result & ValidateLockFlags.INVALID_LENGTH)
139                 .isEqualTo(ValidateLockFlags.INVALID_LENGTH);
140     }
141 
142     @Test
testCheckValidLockPattern_longEnough()143     public void testCheckValidLockPattern_longEnough() throws RemoteException {
144         IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
145                 mInitialLockSetupService.onBind(new Intent()));
146         byte[] pattern = new byte[LockPatternUtils.MIN_LOCK_PATTERN_SIZE + 1];
147         for (int i = 0; i < pattern.length; i++) {
148             pattern[i] = (byte) i;
149         }
150         int result = service.checkValidLock(LockTypes.PATTERN, pattern);
151         assertThat(result & ValidateLockFlags.INVALID_LENGTH)
152                 .isNotEqualTo(ValidateLockFlags.INVALID_LENGTH);
153     }
154 
155     @Test
testSetLockPassword_doesNotWorkWithExistingPassword()156     public void testSetLockPassword_doesNotWorkWithExistingPassword() throws RemoteException {
157         IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
158                 mInitialLockSetupService.onBind(new Intent()));
159         ShadowLockPatternUtils.setPasswordQuality(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
160         int result = service.setLock(LockTypes.PASSWORD, "password".getBytes());
161         assertThat(result).isEqualTo(SetLockCodes.FAIL_LOCK_EXISTS);
162     }
163 
164     @Test
testSetLockPassword_doesNotWorkWithInvalidPassword()165     public void testSetLockPassword_doesNotWorkWithInvalidPassword() throws RemoteException {
166         IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
167                 mInitialLockSetupService.onBind(new Intent()));
168         int result = service.setLock(LockTypes.PASSWORD, "hi".getBytes());
169         assertThat(result).isEqualTo(SetLockCodes.FAIL_LOCK_INVALID);
170     }
171 
172     @Test
testSetLockPassword_setsDevicePassword()173     public void testSetLockPassword_setsDevicePassword() throws RemoteException {
174         IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
175                 mInitialLockSetupService.onBind(new Intent()));
176         byte[] password = "password".getBytes();
177         // Need copy since password is cleared.
178         byte[] expectedPassword = Arrays.copyOf(password, password.length);
179         int result = service.setLock(LockTypes.PASSWORD, password);
180         assertThat(result).isEqualTo(SetLockCodes.SUCCESS);
181         assertThat(Arrays.equals(ShadowLockPatternUtils.getSavedPassword(),
182                 expectedPassword)).isTrue();
183     }
184 
185     @Test
testSetLockPin_setsDevicePin()186     public void testSetLockPin_setsDevicePin() throws RemoteException {
187         IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
188                 mInitialLockSetupService.onBind(new Intent()));
189         byte[] password = "1580".getBytes();
190         byte[] expectedPassword = Arrays.copyOf(password, password.length);
191         int result = service.setLock(LockTypes.PIN, password);
192         assertThat(result).isEqualTo(SetLockCodes.SUCCESS);
193         assertThat(Arrays.equals(ShadowLockPatternUtils.getSavedPassword(),
194                 expectedPassword)).isTrue();
195     }
196 
197     @Test
testSetLockPattern_setsDevicePattern()198     public void testSetLockPattern_setsDevicePattern() throws RemoteException {
199         IInitialLockSetupService service = IInitialLockSetupService.Stub.asInterface(
200                 mInitialLockSetupService.onBind(new Intent()));
201         List<LockPatternView.Cell> pattern = new ArrayList<>();
202         pattern.add(LockPatternView.Cell.of(0, 0));
203         pattern.add(LockPatternView.Cell.of(1, 0));
204         pattern.add(LockPatternView.Cell.of(2, 0));
205         pattern.add(LockPatternView.Cell.of(0, 1));
206         byte[] patternBytes = new byte[pattern.size()];
207         for (int i = 0; i < patternBytes.length; i++) {
208             LockPatternView.Cell cell = pattern.get(i);
209             patternBytes[i] = InitialLockSetupHelper.getByteFromPatternCell(cell.getRow(),
210                     cell.getColumn());
211         }
212         int result = service.setLock(LockTypes.PATTERN, patternBytes);
213         assertThat(result).isEqualTo(SetLockCodes.SUCCESS);
214         List<LockPatternView.Cell> savedPattern = ShadowLockPatternUtils.getSavedPattern();
215         assertThat(savedPattern).containsExactlyElementsIn(pattern);
216     }
217 
218     @Test
testBindFails_ifNoPermissionGranted()219     public void testBindFails_ifNoPermissionGranted() {
220         ShadowContextWrapper shadowContextWrapper = Shadows.shadowOf((ContextWrapper) mContext);
221         shadowContextWrapper.denyPermissions(LOCK_PERMISSION);
222         assertThat(mInitialLockSetupService.onBind(new Intent())).isNull();
223     }
224 
225 }
226