blob: f186b6cb131b25d0973c68ff6b44888b5c4ab2aa [file] [log] [blame]
/*
* Copyright (C) 2022 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.clockwork.power;
import static com.android.clockwork.power.IWearPowerService.OFFLOAD_BACKEND_TYPE_DISPLAYOFFLOAD;
import static com.android.clockwork.power.IWearPowerService.OFFLOAD_BACKEND_TYPE_NA;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.test.core.app.ApplicationProvider;
import com.android.clockwork.flags.BooleanFlag;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowSettings;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowServiceManagerExtended.class, ShadowSettings.class})
public class WearPowerServiceTest {
class OffsettableClock {
private long mOffset = 0L;
public long now() {
return mOffset;
}
public void advance(long timeMs) {
mOffset += timeMs;
}
public void reset() {
mOffset = 0;
}
}
@Mock
private AmbientConfig mAmbientConfigMock;
@Mock
private PowerTracker mPowerTrackerMock;
@Mock
private TimeOnlyMode mTimeOnlyModeMock;
@Mock
private WearTouchMediator mWearTouchMediatorMock;
@Mock
private WearBurnInProtectionMediator mWearBurnInProtectionMediatorMock;
@Mock
private WearDisplayOffloadMediator mWearDisplayOffloadMediatorMock;
@Mock
private WearCoreControlMediator mWearCoreControlMediatorMock;
@Mock
private PowerManager mPowerManagerMock;
@Mock
private PowerManagerInternal mPowerManagerInternalMock;
@Spy
private Context mContextSpy;
private WearPowerService mService;
private WearPowerServiceInternal mLocalService;
private com.android.clockwork.power.IWearPowerService mBinderService;
private BroadcastReceiver mGoToSleepReceiver;
private ContentObserver mSettingObserver;
private OffsettableClock mClock;
private static final int POWER_BUTTON_SUPPRESSION_DELAY_TEST_DEFAULT_MS = 1000;
private static final String ACTION_GO_TO_SLEEEP =
"com.android.clockwork.action.GO_TO_SLEEP";
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContextSpy = spy(ApplicationProvider.getApplicationContext());
mClock = new OffsettableClock();
ContentResolver cr = spy(mContextSpy.getContentResolver());
when(mContextSpy.getContentResolver()).thenReturn(cr);
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(mPowerManagerMock);
Settings.Global.putInt(mContextSpy.getContentResolver(),
Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE,
POWER_BUTTON_SUPPRESSION_DELAY_TEST_DEFAULT_MS);
// enable Core Control Mediator
SystemProperties.set(WearCoreControlMediator.SYSPROP_CORE_CONTROL_ENABLE, "true");
LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
}
@After
public void tearDown() {
LocalServices.removeServiceForTest(PowerManagerInternal.class);
LocalServices.removeServiceForTest(WearPowerServiceInternal.class);
ShadowServiceManagerExtended.reset();
ShadowSettings.reset();
}
private void initService() {
mService = new WearPowerService(mContextSpy, new WearPowerService.Injector() {
@Override
AmbientConfig createAmbientConfig(ContentResolver contentResolver) {
return mAmbientConfigMock;
}
@Override
PowerTracker createPowerTracker(Context context, PowerManager powerManager) {
return mPowerTrackerMock;
}
@Override
TimeOnlyMode createTimeOnlyMode(ContentResolver contentResolver,
PowerTracker powerTracker) {
return mTimeOnlyModeMock;
}
@Override
WearTouchMediator createWearTouchMediator(Context context, AmbientConfig ambientConfig,
PowerTracker powerTracker, TimeOnlyMode timeOnlyMode,
BooleanFlag userAbsentTouchOff) {
return mWearTouchMediatorMock;
}
@Override
WearBurnInProtectionMediator createWearBurnInProtectionMediator(Context context,
AmbientConfig ambientConfig) {
return mWearBurnInProtectionMediatorMock;
}
@Override
WearDisplayOffloadMediator createWearDisplayOffloadMediator(Context context) {
return mWearDisplayOffloadMediatorMock;
}
@Override
WearCoreControlMediator createWearCoreControlMediator(Context context,
PowerTracker powerTracker) {
return mWearCoreControlMediatorMock;
}
@Override
WearPowerService.Clock createClock() {
return () -> mClock.now();
}
});
ArgumentCaptor<BroadcastReceiver> goToSleepReceiverCaptor = ArgumentCaptor.forClass(
BroadcastReceiver.class);
ArgumentCaptor<ContentObserver> settingObserverCaptor = ArgumentCaptor.forClass(
ContentObserver.class);
mService.onStart();
mBinderService = IWearPowerService.Stub.asInterface(
ServiceManager.getService(WearPowerService.SERVICE_NAME));
mLocalService = LocalServices.getService(WearPowerServiceInternal.class);
verify(mContextSpy).registerReceiver(goToSleepReceiverCaptor.capture(), any(), any(),
any(), eq(Context.RECEIVER_EXPORTED));
mGoToSleepReceiver = goToSleepReceiverCaptor.getValue();
assertNotNull(mBinderService);
assertNotNull(mLocalService);
assertNotNull(mGoToSleepReceiver);
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
verify(mContextSpy.getContentResolver()).registerContentObserver(isA(Uri.class),
anyBoolean(), settingObserverCaptor.capture(), eq(
UserHandle.USER_ALL));
mSettingObserver = settingObserverCaptor.getValue();
assertNotNull(mSettingObserver);
mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
verify(mAmbientConfigMock, times(1)).register();
verify(mPowerTrackerMock, times(1)).onBootCompleted();
verify(mWearTouchMediatorMock, times(1)).onBootCompleted();
verify(mWearCoreControlMediatorMock, times(1)).onBootCompleted();
}
@Test
public void testBinderInterface_offloadBackendGetType() throws RemoteException {
initService();
int offloadBackend = OFFLOAD_BACKEND_TYPE_DISPLAYOFFLOAD;
when(mWearDisplayOffloadMediatorMock.offloadBackendGetType()).thenReturn(offloadBackend);
assertEquals(offloadBackend, mBinderService.offloadBackendGetType());
}
@Test
public void testBinderInterface_offloadBackendGetType_nullMediator() throws RemoteException {
mWearDisplayOffloadMediatorMock = null;
initService();
assertEquals(OFFLOAD_BACKEND_TYPE_NA, mBinderService.offloadBackendGetType());
}
@Test
public void testBinderInterface_offloadBackendReadyToDisplay_nullMediator()
throws RemoteException {
mWearDisplayOffloadMediatorMock = null;
initService();
assertFalse(mBinderService.offloadBackendReadyToDisplay());
}
@Test
public void testBinderInterface_offloadBackendReadyToDisplay_Ready() throws RemoteException {
initService();
when(mWearDisplayOffloadMediatorMock.offloadBackendReadyToDisplay()).thenReturn(true);
assertTrue(mBinderService.offloadBackendReadyToDisplay());
}
@Test
public void testBinderInterface_offloadBackendReadyToDisplay_NotReady() throws RemoteException {
initService();
when(mWearDisplayOffloadMediatorMock.offloadBackendReadyToDisplay()).thenReturn(false);
assertFalse(mBinderService.offloadBackendReadyToDisplay());
}
@Test
public void testBinderInterface_offloadBackendSetShouldControlDisplay() throws RemoteException {
initService();
mBinderService.offloadBackendSetShouldControlDisplay(true);
verify(mWearDisplayOffloadMediatorMock).setShouldControlDisplay(eq(true));
}
@Test
public void testLocalService_getAmbientConfig() {
initService();
assertEquals(mAmbientConfigMock, mLocalService.getAmbientConfig());
}
@Test
public void testLocalService_getPowerTracker() {
initService();
assertEquals(mPowerTrackerMock, mLocalService.getPowerTracker());
}
@Test
public void testLocalService_getTimeOnlyMode() {
initService();
assertEquals(mTimeOnlyModeMock, mLocalService.getTimeOnlyMode());
}
@Test
public void testUpdatePowerButtonSettings() {
final int suppressionDelayMs = 2000;
initService();
Settings.Global.putInt(mContextSpy.getContentResolver(),
Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE,
suppressionDelayMs);
mSettingObserver.onChange(true);
assertEquals(suppressionDelayMs, mService.getPowerButtonSuppressionDelayMsForTest());
}
@Test
public void testGoToSleep_WokenByGesture_ButtonPressedAfterSuppressionDelay() {
initService();
// set last wake reason
PowerManager.WakeData wd = new PowerManager.WakeData(mClock.now(),
PowerManager.WAKE_REASON_GESTURE, 1000);
// advance the system clock by 5000ms so that now() > lastWake + suppressionDelay
mClock.advance(5000);
when(mPowerManagerInternalMock.getLastWakeup()).thenReturn(wd);
Intent sleepIntent = new Intent(ACTION_GO_TO_SLEEEP);
mGoToSleepReceiver.onReceive(mContextSpy, sleepIntent);
// device should be put to sleep
verify(mPowerManagerMock).goToSleep(eq(mClock.now()),
eq(PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON), anyInt());
}
@Test
public void testGoToSleep_WokenByGesture_ButtonPressedBeforeSuppressionDelay() {
initService();
// set last wake reason
PowerManager.WakeData wd = new PowerManager.WakeData(mClock.now(),
PowerManager.WAKE_REASON_GESTURE, 1000);
// advance the system clock by 200ms so that now() < lastWake + suppressionDelay
mClock.advance(200);
when(mPowerManagerInternalMock.getLastWakeup()).thenReturn(wd);
Intent sleepIntent = new Intent(ACTION_GO_TO_SLEEEP);
mGoToSleepReceiver.onReceive(mContextSpy, sleepIntent);
// device should not be put to sleep
verify(mPowerManagerMock, never()).goToSleep(anyLong(), anyInt(), anyInt());
}
}