1 /*
2  * Copyright (C) 2023 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.devicelockcontroller;
18 
19 import static com.android.devicelockcontroller.common.DeviceLockConstants.EXTRA_KIOSK_PACKAGE;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.mockito.ArgumentMatchers.anyBoolean;
24 import static org.mockito.ArgumentMatchers.eq;
25 import static org.mockito.Mockito.verify;
26 import static org.mockito.Mockito.when;
27 
28 import android.content.ComponentName;
29 import android.content.Intent;
30 import android.os.Bundle;
31 import android.os.IBinder;
32 import android.os.RemoteCallback;
33 import android.os.RemoteException;
34 
35 import androidx.test.core.app.ApplicationProvider;
36 import androidx.test.rule.ServiceTestRule;
37 
38 import com.android.devicelockcontroller.policy.DevicePolicyController;
39 import com.android.devicelockcontroller.policy.DeviceStateController;
40 import com.android.devicelockcontroller.policy.FinalizationController;
41 import com.android.devicelockcontroller.stats.StatsLogger;
42 import com.android.devicelockcontroller.stats.StatsLoggerProvider;
43 import com.android.devicelockcontroller.storage.SetupParametersClient;
44 
45 import com.google.common.util.concurrent.Futures;
46 
47 import org.junit.Before;
48 import org.junit.Rule;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 import org.robolectric.Robolectric;
52 import org.robolectric.RobolectricTestRunner;
53 import org.robolectric.Shadows;
54 import org.robolectric.shadows.ShadowApplication;
55 import org.robolectric.shadows.ShadowPackageManager;
56 
57 import java.util.concurrent.ExecutionException;
58 import java.util.concurrent.TimeoutException;
59 
60 @RunWith(RobolectricTestRunner.class)
61 public final class DeviceLockControllerServiceTest {
62 
63     @Rule
64     public final ServiceTestRule mServiceRule = new ServiceTestRule();
65 
66     private static final int KIOSK_APP_UID = 123;
67 
68     private static final String KIOSK_APP_PACKAGE_NAME = "TEST_PACKAGE";
69 
70     private StatsLogger mStatsLogger;
71     private TestDeviceLockControllerApplication mTestApp;
72 
73     @Before
setUp()74     public void setUp() throws TimeoutException, ExecutionException, InterruptedException {
75         mTestApp = ApplicationProvider.getApplicationContext();
76         StatsLoggerProvider loggerProvider =
77                 (StatsLoggerProvider) mTestApp.getApplicationContext();
78         mStatsLogger = loggerProvider.getStatsLogger();
79 
80         // Put Kiosk app package name and UID into SetupParameters and shadow PackageManager
81         setupSetupParameters();
82         ShadowPackageManager packageManager = Shadows.shadowOf(mTestApp.getPackageManager());
83         packageManager.setPackagesForUid(KIOSK_APP_UID, KIOSK_APP_PACKAGE_NAME);
84         final ShadowApplication shadowApplication = Shadows.shadowOf(mTestApp);
85 
86         // Setup the service for DeviceLockControllerService using Robolectric
87         DeviceLockControllerService dlcService = Robolectric.setupService(
88                 DeviceLockControllerService.class);
89         shadowApplication.setComponentNameAndServiceForBindService(
90                 new ComponentName(mTestApp, DeviceLockControllerService.class),
91                 dlcService.onBind(/* intent =*/null));
92         shadowApplication.setBindServiceCallsOnServiceConnectedDirectly(/* callDirectly =*/true);
93     }
94 
95     @Test
lockDevice_shouldLogKioskRequest_andLogLockSuccess()96     public void lockDevice_shouldLogKioskRequest_andLogLockSuccess() throws RemoteException,
97             TimeoutException {
98         Intent serviceIntent = new Intent(mTestApp, DeviceLockControllerService.class);
99         IBinder binder = mServiceRule.bindService(serviceIntent);
100         when(mTestApp.getDeviceStateController().lockDevice()).thenReturn(
101                 Futures.immediateVoidFuture());
102 
103         assertThat(binder).isNotNull();
104 
105         IDeviceLockControllerService.Stub serviceStub = (IDeviceLockControllerService.Stub) binder;
106         serviceStub.lockDevice(new RemoteCallback((result -> {})));
107 
108         verify(mStatsLogger).logKioskAppRequest(eq(KIOSK_APP_UID));
109         verify(mStatsLogger).logSuccessfulLockingDevice();
110     }
111 
112     @Test
lockDevice_failure_shouldLogToStatsLogger()113     public void lockDevice_failure_shouldLogToStatsLogger() throws TimeoutException,
114             RemoteException {
115         DeviceStateController deviceStateController = mTestApp.getDeviceStateController();
116         Intent serviceIntent = new Intent(mTestApp, DeviceLockControllerService.class);
117         IBinder binder = mServiceRule.bindService(serviceIntent);
118         when(deviceStateController.lockDevice()).thenReturn(
119                 Futures.immediateFailedFuture(new RuntimeException("Test Exception")));
120         when(deviceStateController.getDeviceState()).thenReturn(Futures.immediateFuture(
121                 DeviceStateController.DeviceState.UNLOCKED));
122 
123         assertThat(binder).isNotNull();
124 
125         IDeviceLockControllerService.Stub serviceStub = (IDeviceLockControllerService.Stub) binder;
126         serviceStub.lockDevice(new RemoteCallback((result -> {})));
127 
128         verify(mStatsLogger).logLockDeviceFailure(DeviceStateController.DeviceState.UNLOCKED);
129     }
130 
131     @Test
unlockDevice_shouldLogKioskRequest_AndLogUnlockSuccess()132     public void unlockDevice_shouldLogKioskRequest_AndLogUnlockSuccess() throws RemoteException,
133             TimeoutException {
134         Intent serviceIntent = new Intent(mTestApp, DeviceLockControllerService.class);
135         IBinder binder = mServiceRule.bindService(serviceIntent);
136         when(mTestApp.getDeviceStateController().unlockDevice()).thenReturn(
137                 Futures.immediateVoidFuture());
138 
139         assertThat(binder).isNotNull();
140 
141         IDeviceLockControllerService.Stub serviceStub = (IDeviceLockControllerService.Stub) binder;
142         serviceStub.unlockDevice(new RemoteCallback((result -> {})));
143 
144         verify(mStatsLogger).logKioskAppRequest(eq(KIOSK_APP_UID));
145         verify(mStatsLogger).logSuccessfulUnlockingDevice();
146     }
147 
148     @Test
unlockDevice_failure_shouldLogToStatsLogger()149     public void unlockDevice_failure_shouldLogToStatsLogger() throws TimeoutException,
150             RemoteException {
151         DeviceStateController deviceStateController = mTestApp.getDeviceStateController();
152         Intent serviceIntent = new Intent(mTestApp, DeviceLockControllerService.class);
153         IBinder binder = mServiceRule.bindService(serviceIntent);
154         when(deviceStateController.unlockDevice()).thenReturn(
155                 Futures.immediateFailedFuture(new RuntimeException("Test Exception")));
156         when(deviceStateController.getDeviceState()).thenReturn(Futures.immediateFuture(
157                 DeviceStateController.DeviceState.LOCKED));
158 
159         assertThat(binder).isNotNull();
160 
161         IDeviceLockControllerService.Stub serviceStub = (IDeviceLockControllerService.Stub) binder;
162         serviceStub.unlockDevice(new RemoteCallback((result -> {})));
163 
164         verify(mStatsLogger).logUnlockDeviceFailure(DeviceStateController.DeviceState.LOCKED);
165     }
166 
167     @Test
isDeviceLocked_shouldLogKioskRequest()168     public void isDeviceLocked_shouldLogKioskRequest() throws RemoteException, TimeoutException {
169         Intent serviceIntent = new Intent(mTestApp, DeviceLockControllerService.class);
170         IBinder binder = mServiceRule.bindService(serviceIntent);
171         when(mTestApp.getDeviceStateController().isLocked()).thenReturn(
172                 Futures.immediateFuture(true));
173 
174         assertThat(binder).isNotNull();
175 
176         IDeviceLockControllerService.Stub serviceStub = (IDeviceLockControllerService.Stub) binder;
177         serviceStub.isDeviceLocked(new RemoteCallback((result -> {})));
178 
179         verify(mStatsLogger).logKioskAppRequest(eq(KIOSK_APP_UID));
180     }
181 
182     @Test
getDeviceIdentifier_shouldLogKioskRequest()183     public void getDeviceIdentifier_shouldLogKioskRequest()
184             throws RemoteException, TimeoutException {
185         Intent serviceIntent = new Intent(mTestApp, DeviceLockControllerService.class);
186         IBinder binder = mServiceRule.bindService(serviceIntent);
187 
188         assertThat(binder).isNotNull();
189 
190         IDeviceLockControllerService.Stub serviceStub = (IDeviceLockControllerService.Stub) binder;
191         serviceStub.getDeviceIdentifier(new RemoteCallback((result -> {})));
192 
193         verify(mStatsLogger).logKioskAppRequest(eq(KIOSK_APP_UID));
194     }
195 
196     @Test
clearDeviceRestrictions_shouldLogKioskRequest()197     public void clearDeviceRestrictions_shouldLogKioskRequest()
198             throws RemoteException, TimeoutException {
199         Intent serviceIntent = new Intent(mTestApp, DeviceLockControllerService.class);
200         IBinder binder = mServiceRule.bindService(serviceIntent);
201         DeviceStateController deviceStateController = mTestApp.getDeviceStateController();
202         when(deviceStateController.clearDevice()).thenReturn(Futures.immediateVoidFuture());
203         FinalizationController finalizationController = mTestApp.getFinalizationController();
204         when(finalizationController.notifyRestrictionsCleared())
205                 .thenReturn(Futures.immediateVoidFuture());
206 
207         assertThat(binder).isNotNull();
208 
209         IDeviceLockControllerService.Stub serviceStub = (IDeviceLockControllerService.Stub) binder;
210         serviceStub.clearDeviceRestrictions(new RemoteCallback((result -> {})));
211 
212         verify(mStatsLogger).logKioskAppRequest(eq(KIOSK_APP_UID));
213     }
214 
215     @Test
onUserSwitching_enforcePoliciesAndFinalizationState()216     public void onUserSwitching_enforcePoliciesAndFinalizationState()
217             throws RemoteException, TimeoutException {
218         Intent serviceIntent = new Intent(mTestApp, DeviceLockControllerService.class);
219         IBinder binder = mServiceRule.bindService(serviceIntent);
220         DevicePolicyController policyController = mTestApp.getPolicyController();
221         when(policyController.enforceCurrentPolicies()).thenReturn(Futures.immediateVoidFuture());
222         FinalizationController finalizationController = mTestApp.getFinalizationController();
223         when(finalizationController.enforceDiskState(anyBoolean())).thenReturn(
224                 Futures.immediateVoidFuture());
225 
226         assertThat(binder).isNotNull();
227         IDeviceLockControllerService.Stub serviceStub = (IDeviceLockControllerService.Stub) binder;
228         serviceStub.onUserSwitching(new RemoteCallback(result -> {}));
229 
230         verify(policyController).enforceCurrentPolicies();
231         verify(finalizationController).enforceDiskState(true);
232     }
233 
setupSetupParameters()234     private static void setupSetupParameters() throws InterruptedException, ExecutionException {
235         Bundle preferences = new Bundle();
236         preferences.putString(EXTRA_KIOSK_PACKAGE, KIOSK_APP_PACKAGE_NAME);
237         SetupParametersClient.getInstance().createPrefs(preferences).get();
238     }
239 }
240