1 /* 2 * Copyright (C) 2018 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.android.car.garagemode.GarageMode.ACTION_GARAGE_MODE_OFF; 20 import static com.android.car.garagemode.GarageMode.ACTION_GARAGE_MODE_ON; 21 import static com.android.car.power.CarPowerManagementService.INVALID_TIMEOUT; 22 23 import static com.google.common.truth.Truth.assertThat; 24 import static com.google.common.truth.Truth.assertWithMessage; 25 26 import static org.mockito.ArgumentMatchers.any; 27 import static org.mockito.ArgumentMatchers.eq; 28 import static org.mockito.Mockito.clearInvocations; 29 import static org.mockito.Mockito.doNothing; 30 import static org.mockito.Mockito.doReturn; 31 import static org.mockito.Mockito.mock; 32 import static org.mockito.Mockito.never; 33 import static org.mockito.Mockito.timeout; 34 import static org.mockito.Mockito.times; 35 import static org.mockito.Mockito.verify; 36 import static org.mockito.Mockito.when; 37 38 import android.car.hardware.power.CarPowerManager; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.res.Resources; 42 import android.os.Handler; 43 import android.os.HandlerThread; 44 import android.os.Looper; 45 import android.os.UserHandle; 46 import android.util.Log; 47 48 import androidx.test.ext.junit.runners.AndroidJUnit4; 49 import androidx.test.filters.SmallTest; 50 51 import com.android.car.CarLocalServices; 52 import com.android.car.power.CarPowerManagementService; 53 import com.android.car.systeminterface.SystemInterface; 54 import com.android.car.user.CarUserService; 55 56 import org.junit.After; 57 import org.junit.Before; 58 import org.junit.Rule; 59 import org.junit.Test; 60 import org.junit.runner.RunWith; 61 import org.mockito.ArgumentCaptor; 62 import org.mockito.Captor; 63 import org.mockito.Mock; 64 import org.mockito.junit.MockitoJUnit; 65 import org.mockito.junit.MockitoRule; 66 67 import java.io.File; 68 import java.io.IOException; 69 import java.nio.file.Files; 70 import java.util.ArrayList; 71 import java.util.List; 72 import java.util.concurrent.Executor; 73 74 @RunWith(AndroidJUnit4.class) 75 @SmallTest 76 public class GarageModeControllerTest { 77 78 private static final String TAG = "GarageModeControllerTest"; 79 private static final int DEFAULT_TIMEOUT_MS = 1000; 80 81 @Rule public final MockitoRule rule = MockitoJUnit.rule(); 82 83 @Mock private Context mContextMock; 84 @Mock private CarUserService mCarUserServiceMock; 85 @Mock private SystemInterface mSystemInterfaceMock; 86 @Mock private CarPowerManagementService mCarPowerManagementServiceMock; 87 private CarUserService mCarUserServiceOriginal; 88 private CarPowerManagementService mCarPowerManagementServiceOriginal; 89 @Captor private ArgumentCaptor<Intent> mIntentCaptor; 90 91 private GarageModeController mController; 92 private File mTempTestDir; 93 private HandlerThread mHandlerThread; 94 private Handler mHandler; 95 private Looper mLooper; 96 97 @Before setUp()98 public void setUp() throws IOException { 99 mHandlerThread = new HandlerThread("ControllerTest"); 100 mHandlerThread.start(); 101 mLooper = mHandlerThread.getLooper(); 102 mHandler = new Handler(mLooper); 103 mCarUserServiceOriginal = CarLocalServices.getService(CarUserService.class); 104 mCarPowerManagementServiceOriginal = CarLocalServices.getService( 105 CarPowerManagementService.class); 106 CarLocalServices.removeServiceForTest(CarUserService.class); 107 CarLocalServices.addService(CarUserService.class, mCarUserServiceMock); 108 CarLocalServices.removeServiceForTest(SystemInterface.class); 109 CarLocalServices.addService(SystemInterface.class, mSystemInterfaceMock); 110 CarLocalServices.removeServiceForTest(CarPowerManagementService.class); 111 CarLocalServices.addService(CarPowerManagementService.class, 112 mCarPowerManagementServiceMock); 113 114 mTempTestDir = Files.createTempDirectory("garagemode_test").toFile(); 115 when(mSystemInterfaceMock.getSystemCarDir()).thenReturn(mTempTestDir); 116 Log.v(TAG, "Using temp dir: %s " + mTempTestDir.getAbsolutePath()); 117 118 mController = new GarageModeController(mContextMock, mLooper, mHandler, 119 /* garageMode= */ null); 120 121 doReturn(new ArrayList<Integer>()).when(mCarUserServiceMock) 122 .startAllBackgroundUsersInGarageMode(); 123 doNothing().when(mSystemInterfaceMock) 124 .sendBroadcastAsUser(any(Intent.class), any(UserHandle.class)); 125 mController.init(); 126 } 127 128 @After tearDown()129 public void tearDown() throws Exception { 130 mHandlerThread.quitSafely(); 131 mHandlerThread.join(); 132 133 CarLocalServices.removeServiceForTest(CarUserService.class); 134 CarLocalServices.addService(CarUserService.class, mCarUserServiceOriginal); 135 CarLocalServices.removeServiceForTest(SystemInterface.class); 136 CarLocalServices.removeServiceForTest(CarPowerManagementService.class); 137 CarLocalServices.addService(CarPowerManagementService.class, 138 mCarPowerManagementServiceOriginal); 139 } 140 141 @Test testOnShutdownPrepare_shouldInitiateGarageMode()142 public void testOnShutdownPrepare_shouldInitiateGarageMode() { 143 // Sending notification that state has changed 144 mController.onStateChanged(CarPowerManager.STATE_SHUTDOWN_PREPARE, INVALID_TIMEOUT); 145 146 // Assert that GarageMode has been started 147 verify(mSystemInterfaceMock, timeout(DEFAULT_TIMEOUT_MS)) 148 .sendBroadcastAsUser(mIntentCaptor.capture(), eq(UserHandle.ALL)); 149 assertThat(mController.isGarageModeActive()).isTrue(); 150 verifyGarageModeBroadcast(mIntentCaptor.getAllValues(), 1, ACTION_GARAGE_MODE_ON); 151 } 152 153 @Test testOnShutdownCancelled_shouldCancelGarageMode()154 public void testOnShutdownCancelled_shouldCancelGarageMode() { 155 Looper looper = Looper.getMainLooper(); 156 mController = new GarageModeController(mContextMock, looper); 157 mController.init(); 158 // Sending notification that state has changed 159 mController.onStateChanged(CarPowerManager.STATE_SHUTDOWN_PREPARE, INVALID_TIMEOUT); 160 161 // Sending shutdown cancelled signal to controller, GarageMode should wrap up and stop 162 mController.onStateChanged(CarPowerManager.STATE_SHUTDOWN_CANCELLED, INVALID_TIMEOUT); 163 164 // Verify that OFF signal broadcasted to JobScheduler 165 verify(mSystemInterfaceMock, timeout(DEFAULT_TIMEOUT_MS).times(2)) 166 .sendBroadcastAsUser(mIntentCaptor.capture(), eq(UserHandle.ALL)); 167 verifyGarageModeBroadcast(mIntentCaptor.getAllValues(), 1, ACTION_GARAGE_MODE_ON); 168 verifyGarageModeBroadcast(mIntentCaptor.getAllValues(), 2, ACTION_GARAGE_MODE_OFF); 169 170 // Verify that listener is completed due to the cancellation. 171 verify(mCarPowerManagementServiceMock, timeout(DEFAULT_TIMEOUT_MS)) 172 .completeHandlingPowerStateChange( 173 eq(CarPowerManager.STATE_SHUTDOWN_PREPARE), eq(mController)); 174 } 175 176 @Test testInitAndRelease()177 public void testInitAndRelease() { 178 Executor mockExecutor = mock(Executor.class); 179 when(mContextMock.getMainExecutor()).thenReturn(mockExecutor); 180 GarageMode garageMode = mock(GarageMode.class); 181 var controller = new GarageModeController(mContextMock, mLooper, mHandler, garageMode); 182 183 controller.init(); 184 controller.release(); 185 186 verify(garageMode).init(); 187 verify(garageMode).release(); 188 } 189 190 @Test testConstructor()191 public void testConstructor() { 192 Resources resourcesMock = mock(Resources.class); 193 when(mContextMock.getResources()).thenReturn(resourcesMock); 194 195 var controller = new GarageModeController(mContextMock, mLooper); 196 197 assertThat(controller).isNotNull(); 198 } 199 200 @Test testOnStateChanged()201 public void testOnStateChanged() { 202 GarageMode garageMode = mock(GarageMode.class); 203 var controller = new GarageModeController(mContextMock, mLooper, mHandler, garageMode); 204 controller.init(); 205 206 controller.onStateChanged(CarPowerManager.STATE_SHUTDOWN_CANCELLED, INVALID_TIMEOUT); 207 verify(garageMode, timeout(DEFAULT_TIMEOUT_MS)).cancel(any()); 208 209 clearInvocations(garageMode); 210 controller.onStateChanged(CarPowerManager.STATE_SHUTDOWN_ENTER, INVALID_TIMEOUT); 211 verify(garageMode, timeout(DEFAULT_TIMEOUT_MS)).cancel(any()); 212 213 clearInvocations(garageMode); 214 controller.onStateChanged(CarPowerManager.STATE_SUSPEND_ENTER, INVALID_TIMEOUT); 215 verify(garageMode, timeout(DEFAULT_TIMEOUT_MS)).cancel(any()); 216 217 clearInvocations(garageMode); 218 controller.onStateChanged(CarPowerManager.STATE_HIBERNATION_ENTER, INVALID_TIMEOUT); 219 verify(garageMode, timeout(DEFAULT_TIMEOUT_MS)).cancel(any()); 220 221 clearInvocations(garageMode); 222 controller.onStateChanged(CarPowerManager.STATE_INVALID , INVALID_TIMEOUT); 223 verify(garageMode, never()).cancel(any()); 224 } 225 verifyGarageModeBroadcast(List<Intent> intents, int times, String action)226 private void verifyGarageModeBroadcast(List<Intent> intents, int times, String action) { 227 // Capture sent intent and verify that it is correct 228 assertWithMessage("no of intents").that(intents.size()).isAtLeast(times); 229 Intent i = intents.get(times - 1); 230 assertWithMessage("intent action on %s", i).that(i.getAction()) 231 .isEqualTo(action); 232 233 // Verify that additional critical flags are bundled as well 234 int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_NO_ABORT; 235 boolean areRequiredFlagsSet = ((flags & i.getFlags()) == flags); 236 assertThat(areRequiredFlagsSet).isTrue(); 237 } 238 } 239