• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.garagemode;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.common.truth.Truth.assertWithMessage;
21 
22 import static org.mockito.ArgumentMatchers.any;
23 import static org.mockito.ArgumentMatchers.anyInt;
24 import static org.mockito.Mockito.doAnswer;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.timeout;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.when;
29 
30 import android.app.job.JobScheduler;
31 import android.car.user.CarUserManager;
32 import android.car.user.CarUserManager.UserLifecycleEvent;
33 import android.car.user.CarUserManager.UserLifecycleListener;
34 import android.content.Context;
35 import android.os.Handler;
36 import android.os.HandlerThread;
37 import android.os.Looper;
38 import android.util.Log;
39 
40 import androidx.test.ext.junit.runners.AndroidJUnit4;
41 import androidx.test.filters.SmallTest;
42 
43 import com.android.car.CarLocalServices;
44 import com.android.car.power.CarPowerManagementService;
45 import com.android.car.systeminterface.SystemInterface;
46 import com.android.car.user.CarUserService;
47 
48 import org.junit.After;
49 import org.junit.Before;
50 import org.junit.Rule;
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 import org.mockito.ArgumentCaptor;
54 import org.mockito.Mock;
55 import org.mockito.junit.MockitoJUnit;
56 import org.mockito.junit.MockitoRule;
57 
58 import java.io.File;
59 import java.io.IOException;
60 import java.nio.file.Files;
61 import java.util.ArrayList;
62 import java.util.Arrays;
63 import java.util.concurrent.CountDownLatch;
64 import java.util.concurrent.TimeUnit;
65 
66 @RunWith(AndroidJUnit4.class)
67 @SmallTest
68 public final class GarageModeTest {
69 
70     private static final int DEFAULT_TIMEOUT_MS = 1000;
71     private static final String TAG = "GarageModeTest";
72 
73     @Rule
74     public final MockitoRule rule = MockitoJUnit.rule();
75     private GarageMode mGarageMode;
76     @Mock
77     private Context mContext;
78     @Mock
79     private GarageModeController mController;
80     @Mock
81     private JobScheduler mJobScheduler;
82     @Mock
83     private CarUserService mCarUserService;
84     private final Handler mHandler = new Handler(Looper.getMainLooper());
85     private final HandlerThread mBgHandlerThread = new HandlerThread("GarageModeTest");
86     private Handler mBgHandler;
87 
88     @Mock
89     private SystemInterface mSystemInterface;
90     private File mTempTestDir;
91 
92     @Before
setUp()93     public void setUp() throws IOException {
94         mBgHandlerThread.start();
95         mBgHandler = new Handler(mBgHandlerThread.getLooper());
96 
97         when(mController.getHandler()).thenReturn(mHandler);
98         when(mContext.getSystemService(JobScheduler.class)).thenReturn(mJobScheduler);
99 
100         CarLocalServices.removeServiceForTest(CarUserService.class);
101         CarLocalServices.addService(CarUserService.class, mCarUserService);
102 
103         CarLocalServices.removeServiceForTest(SystemInterface.class);
104         CarLocalServices.addService(SystemInterface.class, mSystemInterface);
105 
106         mTempTestDir = Files.createTempDirectory("garagemode_test").toFile();
107         when(mSystemInterface.getSystemCarDir()).thenReturn(mTempTestDir);
108         Log.v(TAG, "Using temp dir: %s " + mTempTestDir.getAbsolutePath());
109 
110         mGarageMode = new GarageMode(mContext, mController);
111 
112         mGarageMode.init();
113     }
114 
115     @After
teardown()116     public void teardown() throws Exception {
117         mBgHandlerThread.quitSafely();
118         mBgHandlerThread.join();
119 
120         CarLocalServices.removeServiceForTest(CarUserService.class);
121     }
122 
123     @Test
test_releaseRemoveListener()124     public void test_releaseRemoveListener() {
125         mGarageMode.release();
126 
127         verify(mCarUserService).removeUserLifecycleListener(any());
128     }
129 
130     @Test
test_backgroundUsersStopedOnGarageModeCancel()131     public void test_backgroundUsersStopedOnGarageModeCancel() throws Exception {
132         ArrayList<Integer> userToStartInBackground = new ArrayList<>(Arrays.asList(101, 102, 103));
133         when(mCarUserService.startAllBackgroundUsersInGarageMode())
134                 .thenReturn(userToStartInBackground);
135         mockCarUserServiceStopUserCall(getEventListener());
136 
137         mHandler.post(() -> {
138             mGarageMode.enterGarageMode(/* completor= */ null);
139         });
140 
141         verify(mCarUserService, timeout(DEFAULT_TIMEOUT_MS)).startAllBackgroundUsersInGarageMode();
142 
143         CountDownLatch latch = new CountDownLatch(1);
144         mHandler.post(() -> {
145             mGarageMode.cancel(() -> latch.countDown());
146         });
147 
148         waitForHandlerThreadToFinish(latch);
149         verify(mCarUserService).startAllBackgroundUsersInGarageMode();
150         assertThat(mGarageMode.getStartedBackgroundUsers()).isEmpty();
151     }
152 
153     @Test
test_backgroundUsersStopedOnGarageModeCancel_beforeStartingBgUsers()154     public void test_backgroundUsersStopedOnGarageModeCancel_beforeStartingBgUsers()
155             throws Exception {
156         ArrayList<Integer> userToStartInBackground = new ArrayList<>(Arrays.asList(101, 102, 103));
157         when(mCarUserService.startAllBackgroundUsersInGarageMode())
158                 .thenReturn(userToStartInBackground);
159         mockCarUserServiceStopUserCall(getEventListener());
160 
161         mHandler.post(() -> {
162             mGarageMode.enterGarageMode(/* completor= */ null);
163         });
164 
165         CountDownLatch latch = new CountDownLatch(1);
166         // It is possible that cancel is called before the background users are started, in this
167         // case the completor must still be called.
168         mHandler.post(() -> {
169             mGarageMode.cancel(() -> latch.countDown());
170         });
171 
172         waitForHandlerThreadToFinish(latch);
173         assertThat(mGarageMode.getStartedBackgroundUsers()).isEmpty();
174     }
175 
176 
177     @Test
test_backgroundUsersStoppedOnGarageModeFinish()178     public void test_backgroundUsersStoppedOnGarageModeFinish() throws Exception {
179         ArrayList<Integer> userToStartInBackground = new ArrayList<>(Arrays.asList(101, 102, 103));
180         when(mCarUserService.startAllBackgroundUsersInGarageMode())
181                 .thenReturn(userToStartInBackground);
182         mockCarUserServiceStopUserCall(getEventListener());
183 
184         CountDownLatch latch = new CountDownLatch(1);
185         mHandler.post(() -> {
186             mGarageMode.enterGarageMode(() -> latch.countDown());
187         });
188 
189         verify(mCarUserService, timeout(DEFAULT_TIMEOUT_MS)).startAllBackgroundUsersInGarageMode();
190 
191         mHandler.post(() -> {
192             mGarageMode.finish();
193         });
194 
195         waitForHandlerThreadToFinish(latch);
196         assertThat(mGarageMode.getStartedBackgroundUsers()).isEmpty();
197     }
198 
199     @Test
test_restartingGarageModeStorePreviouslyStartedUsers()200     public void test_restartingGarageModeStorePreviouslyStartedUsers() throws Exception {
201         ArrayList<Integer> userToStartInBackground = new ArrayList<>(Arrays.asList(101, 102, 103));
202         CountDownLatch latch = mockCarUserServiceStartUsersCall(userToStartInBackground);
203         mGarageMode.enterGarageMode(/* completor= */ null);
204 
205         waitForHandlerThreadToFinish(latch);
206         assertThat(mGarageMode.getStartedBackgroundUsers()).containsExactly(101, 102, 103);
207 
208         userToStartInBackground = new ArrayList<>(Arrays.asList(103, 104, 105));
209         latch = mockCarUserServiceStartUsersCall(userToStartInBackground);
210         mGarageMode.enterGarageMode(/* completor= */ null);
211 
212         waitForHandlerThreadToFinish(latch);
213         assertThat(mGarageMode.getStartedBackgroundUsers())
214                 .containsExactly(101, 102, 103, 104, 105);
215     }
216 
217     @Test
test_garageModeTestExitImmediately()218     public void test_garageModeTestExitImmediately() throws Exception {
219         CarPowerManagementService mockCarPowerManagementService =
220                 mock(CarPowerManagementService.class);
221 
222         // Mock CPMS to force Garage Mode early exit
223         CarLocalServices.removeServiceForTest(CarPowerManagementService.class);
224         CarLocalServices.addService(CarPowerManagementService.class, mockCarPowerManagementService);
225         when(mockCarPowerManagementService.garageModeShouldExitImmediately()).thenReturn(true);
226 
227         // Check exit immediately without completor
228         GarageMode garageMode = new GarageMode(mContext, mController);
229         garageMode.init();
230         garageMode.enterGarageMode(/* completor= */ null);
231         assertThat(garageMode.isGarageModeActive()).isFalse();
232 
233         // Create new instance of GarageMode
234         garageMode = new GarageMode(mContext, mController);
235         garageMode.init();
236         // Check exit immediately with completor
237         CompletorImpl completor = new CompletorImpl();
238         garageMode.enterGarageMode(completor);
239         assertThat(garageMode.isGarageModeActive()).isFalse();
240         assertThat(completor.isFinished()).isTrue();
241 
242         CarLocalServices.removeServiceForTest(CarPowerManagementService.class);
243     }
244 
waitForHandlerThreadToFinish(CountDownLatch latch)245     private void waitForHandlerThreadToFinish(CountDownLatch latch) throws Exception {
246         assertWithMessage("Latch has timed out.")
247                 .that(latch.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
248         mHandler.runWithScissors(() -> {}, DEFAULT_TIMEOUT_MS);
249     }
250 
mockCarUserServiceStartUsersCall( ArrayList<Integer> userToStartInBackground)251     private CountDownLatch mockCarUserServiceStartUsersCall(
252             ArrayList<Integer> userToStartInBackground) {
253         CountDownLatch latch = new CountDownLatch(1);
254         doAnswer(inv -> {
255             latch.countDown();
256             return userToStartInBackground;
257         }).when(mCarUserService).startAllBackgroundUsersInGarageMode();
258 
259         return latch;
260     }
261 
getEventListener()262     private UserLifecycleListener getEventListener() {
263         ArgumentCaptor<UserLifecycleListener> listenerCaptor =
264                 ArgumentCaptor.forClass(UserLifecycleListener.class);
265         verify(mCarUserService).addUserLifecycleListener(any(), listenerCaptor.capture());
266         UserLifecycleListener listener = listenerCaptor.getValue();
267         return listener;
268     }
269 
mockCarUserServiceStopUserCall(UserLifecycleListener listener)270     private void mockCarUserServiceStopUserCall(UserLifecycleListener listener) {
271         doAnswer(inv -> {
272             int userId = (int) inv.getArguments()[0];
273             mBgHandler.post(() -> listener.onEvent(new UserLifecycleEvent(
274                     CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPED, userId)));
275             return true;
276         }).when(mCarUserService).stopBackgroundUserInGagageMode(anyInt());
277     }
278 
279     private static final class CompletorImpl implements Runnable {
280         private boolean mFinished;
281 
282         @Override
run()283         public void run() {
284             mFinished = true;
285         }
286 
isFinished()287         public boolean isFinished() {
288             return mFinished;
289         }
290     }
291 }
292 
293