| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.car.garagemode; |
| |
| import static com.android.car.garagemode.GarageMode.ACTION_GARAGE_MODE_OFF; |
| import static com.android.car.garagemode.GarageMode.ACTION_GARAGE_MODE_ON; |
| import static com.android.car.garagemode.GarageMode.JOB_SNAPSHOT_INITIAL_UPDATE_MS; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.doNothing; |
| import static org.mockito.Mockito.doReturn; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| |
| import android.car.Car; |
| import android.car.hardware.power.CarPowerManager; |
| import android.car.hardware.power.CarPowerManager.CarPowerStateListener; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.UserHandle; |
| |
| import androidx.test.ext.junit.runners.AndroidJUnit4; |
| import androidx.test.filters.SmallTest; |
| |
| import com.android.car.CarLocalServices; |
| import com.android.car.systeminterface.SystemInterface; |
| import com.android.car.user.CarUserService; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.Captor; |
| import org.mockito.Mock; |
| import org.mockito.Mockito; |
| import org.mockito.junit.MockitoJUnit; |
| import org.mockito.junit.MockitoRule; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.concurrent.CompletableFuture; |
| |
| @RunWith(AndroidJUnit4.class) |
| @SmallTest |
| public class ControllerTest { |
| |
| @Rule public final MockitoRule rule = MockitoJUnit.rule(); |
| |
| @Mock private Context mContextMock; |
| @Mock private Looper mLooperMock; |
| @Mock private Handler mHandlerMock; |
| @Mock private Car mCarMock; |
| @Mock private CarPowerManager mCarPowerManagerMock; |
| @Mock private CarUserService mCarUserServiceMock; |
| @Mock private SystemInterface mSystemInterfaceMock; |
| private CarUserService mCarUserServiceOriginal; |
| private SystemInterface mSystemInterfaceOriginal; |
| @Captor private ArgumentCaptor<Intent> mIntentCaptor; |
| @Captor private ArgumentCaptor<Integer> mIntegerCaptor; |
| |
| private static final String[] sTemplateWakeupSchedule = new String[] { |
| "15m,1", |
| "6h,4", |
| "1d,1"}; |
| private static final int[] sTemplateWakeupScheduleSeconds = new int[] { |
| 15 * 60, |
| 6 * 60 * 60, |
| 6 * 60 * 60, |
| 6 * 60 * 60, |
| 6 * 60 * 60, |
| 24 * 60 * 60}; |
| private Controller mController; |
| private WakeupPolicy mWakeupPolicy; |
| private CompletableFuture<Void> mFuture; |
| |
| @Before |
| public void setUp() { |
| mWakeupPolicy = new WakeupPolicy(sTemplateWakeupSchedule); |
| mController = new Controller( |
| mContextMock, |
| mLooperMock, |
| mWakeupPolicy, |
| mHandlerMock, |
| null); |
| mController.setCarPowerManager(mCarPowerManagerMock); |
| mFuture = new CompletableFuture<>(); |
| mCarUserServiceOriginal = CarLocalServices.getService(CarUserService.class); |
| CarLocalServices.removeServiceForTest(CarUserService.class); |
| CarLocalServices.addService(CarUserService.class, mCarUserServiceMock); |
| CarLocalServices.removeServiceForTest(SystemInterface.class); |
| CarLocalServices.addService(SystemInterface.class, mSystemInterfaceMock); |
| doReturn(new ArrayList<Integer>()).when(mCarUserServiceMock) |
| .startAllBackgroundUsersInGarageMode(); |
| doNothing().when(mSystemInterfaceMock) |
| .sendBroadcastAsUser(any(Intent.class), any(UserHandle.class)); |
| } |
| |
| @After |
| public void tearDown() { |
| CarLocalServices.removeServiceForTest(CarUserService.class); |
| CarLocalServices.addService(CarUserService.class, mCarUserServiceOriginal); |
| CarLocalServices.removeServiceForTest(SystemInterface.class); |
| CarLocalServices.addService(SystemInterface.class, mSystemInterfaceOriginal); |
| } |
| |
| @Test |
| public void testOnShutdownPrepare_shouldInitiateGarageMode() { |
| startAndAssertGarageModeWithSignal(CarPowerStateListener.SHUTDOWN_PREPARE); |
| verify(mSystemInterfaceMock) |
| .sendBroadcastAsUser(mIntentCaptor.capture(), eq(UserHandle.ALL)); |
| verifyGarageModeBroadcast(mIntentCaptor.getAllValues(), 1, ACTION_GARAGE_MODE_ON); |
| } |
| |
| @Test |
| public void testOnShutdownCancelled_shouldCancelGarageMode() { |
| startAndAssertGarageModeWithSignal(CarPowerStateListener.SHUTDOWN_PREPARE); |
| |
| // Sending shutdown cancelled signal to controller, GarageMode should wrap up and stop |
| mController.onStateChanged(CarPowerStateListener.SHUTDOWN_CANCELLED, null); |
| |
| // Verify that wake up counter is reset and GarageMode is not active anymore |
| assertThat(mController.mWakeupPolicy.mIndex).isEqualTo(0); |
| assertThat(mController.isGarageModeActive()).isFalse(); |
| |
| // Verify that monitoring thread has stopped |
| verify(mHandlerMock, Mockito.atLeastOnce()).removeCallbacks(any(Runnable.class)); |
| |
| // Verify that OFF signal broadcasted to JobScheduler |
| verify(mSystemInterfaceMock, times(2)) |
| .sendBroadcastAsUser(mIntentCaptor.capture(), eq(UserHandle.ALL)); |
| verifyGarageModeBroadcast(mIntentCaptor.getAllValues(), 1, ACTION_GARAGE_MODE_ON); |
| verifyGarageModeBroadcast(mIntentCaptor.getAllValues(), 2, ACTION_GARAGE_MODE_OFF); |
| |
| // Verify that bounded future got cancelled |
| assertThat(mFuture.isDone()).isTrue(); |
| assertThat(mFuture.isCancelled()).isTrue(); |
| } |
| |
| @Test |
| public void testWakeupTimeProgression() { |
| // Doing initial suspend |
| startAndAssertGarageModeWithSignal(CarPowerStateListener.SHUTDOWN_PREPARE); |
| |
| // Finish GarageMode and check next scheduled time |
| mController.finishGarageMode(); |
| assertThat(mController.isGarageModeActive()).isFalse(); |
| |
| // Start GarageMode again without waking up car |
| mFuture = new CompletableFuture<>(); |
| mController.onStateChanged(CarPowerStateListener.SHUTDOWN_PREPARE, mFuture); |
| |
| assertThat(mController.mWakeupPolicy.mIndex).isEqualTo(2); |
| assertThat(mController.isGarageModeActive()).isTrue(); |
| |
| // Finish GarageMode and check next scheduled time |
| mController.finishGarageMode(); |
| assertThat(mController.isGarageModeActive()).isFalse(); |
| |
| for (int i = 0; i < 4; i++) { |
| mFuture = new CompletableFuture<>(); |
| mController.onStateChanged(CarPowerStateListener.SHUTDOWN_PREPARE, mFuture); |
| mController.finishGarageMode(); |
| assertThat(mController.isGarageModeActive()).isFalse(); |
| assertThat(mController.mWakeupPolicy.mIndex).isEqualTo(i + 3); |
| } |
| verify(mCarPowerManagerMock, times(6)).scheduleNextWakeupTime(mIntegerCaptor.capture()); |
| verifyScheduledTimes(mIntegerCaptor.getAllValues()); |
| } |
| |
| private void verifyGarageModeBroadcast(List<Intent> intents, int times, String action) { |
| // Capture sent intent and verify that it is correct |
| Intent i = intents.get(times - 1); |
| assertThat(i.getAction()).isEqualTo(action); |
| |
| // Verify that additional critical flags are bundled as well |
| final int flags = Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_NO_ABORT; |
| boolean areRequiredFlagsSet = ((flags & i.getFlags()) == flags); |
| assertThat(areRequiredFlagsSet).isTrue(); |
| } |
| |
| private void verifyScheduledTimes(List<Integer> ints) { |
| int idx = 0; |
| for (int i : ints) { |
| assertThat(i).isEqualTo(sTemplateWakeupScheduleSeconds[idx++]); |
| } |
| } |
| |
| private void startAndAssertGarageModeWithSignal(int signal) { |
| assertThat(mController.mWakeupPolicy.mIndex).isEqualTo(0); |
| |
| // Sending notification that state has changed |
| mController.onStateChanged(signal, mFuture); |
| |
| // Assert that GarageMode has been started |
| assertThat(mController.isGarageModeActive()).isTrue(); |
| assertThat(mController.mWakeupPolicy.mIndex).isEqualTo(1); |
| |
| // Verify that worker that polls running jobs from JobScheduler is scheduled. |
| verify(mHandlerMock).postDelayed(any(), eq(JOB_SNAPSHOT_INITIAL_UPDATE_MS)); |
| } |
| } |