blob: 6c87a36f27cb76eece4d4c53bbd46f4e0e99e194 [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 android.os.PowerManager.WAKE_REASON_APPLICATION;
import static com.android.clockwork.power.IWearPowerService.OFFLOAD_BACKEND_TYPE_DISPLAYOFFLOAD;
import static com.android.clockwork.power.IWearPowerService.OFFLOAD_BACKEND_TYPE_NA;
import static com.android.clockwork.power.IWearPowerService.OFFLOAD_BACKEND_TYPE_SIDEKICK;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.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.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.Color;
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.view.Display;
import com.google.android.clockwork.ambient.offload.IDisplayOffloadService;
import com.google.android.clockwork.sidekick.ISidekickService;
import com.google.android.clockwork.sidekick.SidekickServiceConstants;
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;
import org.robolectric.shadows.ShadowSystemProperties;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowServiceManagerExtended.class, ShadowSettings.class})
public class WearDisplayOffloadMediatorTest {
private static final String CLOCKWORK_SYSUI_PACKAGE_NAME = "SysUiPackage";
@Mock
IDisplayOffloadService.Stub mDisplayOffloadServiceMock;
@Mock
ISidekickService.Stub mSidekickServiceMock;
@Mock
IActivityTaskManager.Stub mActivityTaskManagerServiceMock;
@Mock
PowerManager mPowerManagerMock;
@Mock
DisplayManager mDisplayManagerMock;
@Mock
WallpaperManager mWallpaperManagerMock;
@Mock
Display mDisplayMock;
@Spy
Context mContextSpy;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
Settings.Global.putString(mContextSpy.getContentResolver(),
Settings.Global.Wearable.CLOCKWORK_SYSUI_PACKAGE, CLOCKWORK_SYSUI_PACKAGE_NAME);
// set 'build' to debug. This makes WearDisplayOffloadMediator produce more logs.
ReflectionHelpers.setStaticField(Build.class, "IS_DEBUGGABLE", true);
// Have Stub.asInterface() and Stub.asBinder() return the mocked interface.
when(mDisplayOffloadServiceMock.queryLocalInterface(anyString())).thenReturn(
mDisplayOffloadServiceMock);
when(mDisplayOffloadServiceMock.asBinder()).thenReturn(mDisplayOffloadServiceMock);
when(mSidekickServiceMock.queryLocalInterface(anyString())).thenReturn(
mSidekickServiceMock);
when(mSidekickServiceMock.asBinder()).thenReturn(mSidekickServiceMock);
when(mActivityTaskManagerServiceMock.queryLocalInterface(anyString())).thenReturn(
mActivityTaskManagerServiceMock);
when(mActivityTaskManagerServiceMock.asBinder()).thenReturn(
mActivityTaskManagerServiceMock);
ActivityTaskManager.RootTaskInfo ri = mock(ActivityTaskManager.RootTaskInfo.class);
ri.topActivity = new ComponentName(CLOCKWORK_SYSUI_PACKAGE_NAME, "SomeSysUiClass.class");
when(mActivityTaskManagerServiceMock.getFocusedRootTaskInfo()).thenReturn(ri);
when(mDisplayManagerMock.getDisplay(anyInt())).thenReturn(mDisplayMock);
when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(mPowerManagerMock);
when(mContextSpy.getSystemService(DisplayManager.class)).thenReturn(mDisplayManagerMock);
when(mContextSpy.getSystemService(WallpaperManager.class)).thenReturn(
mWallpaperManagerMock);
ServiceManager.addService(Context.ACTIVITY_TASK_SERVICE, mActivityTaskManagerServiceMock);
}
@After
public void tearDown() {
ShadowSettings.reset();
ShadowSystemProperties.reset();
ShadowServiceManagerExtended.reset();
}
private void enableDisplayOffloadService() {
ServiceManager.addService(IDisplayOffloadService.NAME, mDisplayOffloadServiceMock);
}
private void enableSidekickService() throws RemoteException {
ServiceManager.addService(SidekickServiceConstants.NAME, mSidekickServiceMock);
when(mSidekickServiceMock.sidekickExists()).thenReturn(true);
}
@Test
public void testDisplayOffloadServiceBackend_offloadBackendGetType() throws RemoteException {
enableDisplayOffloadService();
WearDisplayOffloadMediator wearDisplayOffloadMediator = new WearDisplayOffloadMediator(
mContextSpy);
assertEquals(wearDisplayOffloadMediator.offloadBackendGetType(),
OFFLOAD_BACKEND_TYPE_DISPLAYOFFLOAD);
}
@Test
public void testDisplayOffloadServiceBackend_setShouldControlDisplay() throws RemoteException {
enableDisplayOffloadService();
WearDisplayOffloadMediator wearDisplayOffloadMediator = new WearDisplayOffloadMediator(
mContextSpy);
wearDisplayOffloadMediator.setShouldControlDisplay(true);
// When the backend is DisplayOffloadService, verify that setShouldControlDisplay()
// is not invoked on the SidekickService backend.
verify(mSidekickServiceMock, never()).setShouldControlDisplay(anyBoolean());
}
@Test
public void testDisplayOffloadServiceBackend_offloadBackendReadyToDisplay()
throws RemoteException {
enableDisplayOffloadService();
WearDisplayOffloadMediator wearDisplayOffloadMediator = new WearDisplayOffloadMediator(
mContextSpy);
when(mDisplayOffloadServiceMock.readyToDisplay()).thenReturn(true);
assertTrue(wearDisplayOffloadMediator.offloadBackendReadyToDisplay());
verify(mDisplayOffloadServiceMock, times(1)).readyToDisplay();
}
@Test
public void testSidekickServiceBackend_offloadBackendGetType() throws RemoteException {
enableSidekickService();
WearDisplayOffloadMediator wearDisplayOffloadMediator = new WearDisplayOffloadMediator(
mContextSpy);
assertEquals(wearDisplayOffloadMediator.offloadBackendGetType(),
OFFLOAD_BACKEND_TYPE_SIDEKICK);
}
@Test
public void testSidekickServiceBackend_setShouldControlDisplay() throws RemoteException {
enableSidekickService();
WearDisplayOffloadMediator wearDisplayOffloadMediator = new WearDisplayOffloadMediator(
mContextSpy);
wearDisplayOffloadMediator.setShouldControlDisplay(true);
verify(mSidekickServiceMock).setShouldControlDisplay(eq(true));
}
@Test
public void testSidekickServiceBackend_offloadBackendReadyToDisplay() throws RemoteException {
enableSidekickService();
WearDisplayOffloadMediator wearDisplayOffloadMediator = new WearDisplayOffloadMediator(
mContextSpy);
when(mSidekickServiceMock.readyToDisplay()).thenReturn(true);
assertTrue(wearDisplayOffloadMediator.offloadBackendReadyToDisplay());
verify(mSidekickServiceMock, times(1)).readyToDisplay();
}
@Test
public void testNoBackend_offloadBackendGetType() {
WearDisplayOffloadMediator wearDisplayOffloadMediator = new WearDisplayOffloadMediator(
mContextSpy);
assertEquals(wearDisplayOffloadMediator.offloadBackendGetType(), OFFLOAD_BACKEND_TYPE_NA);
}
@Test
public void testNoBackend_setShouldControlDisplay() throws RemoteException {
WearDisplayOffloadMediator wearDisplayOffloadMediator = new WearDisplayOffloadMediator(
mContextSpy);
wearDisplayOffloadMediator.setShouldControlDisplay(true);
verify(mSidekickServiceMock, never()).setShouldControlDisplay(anyBoolean());
}
@Test
public void testNoBackend_offloadBackendReadyToDisplay() {
WearDisplayOffloadMediator wearDisplayOffloadMediator = new WearDisplayOffloadMediator(
mContextSpy);
assertFalse(wearDisplayOffloadMediator.offloadBackendReadyToDisplay());
}
@Test
public void testColorChangeListener() {
enableDisplayOffloadService();
// invoke the constructor to register the color change listener. We don't need a reference
// to the WearDisplayOffloadMediator object for this test.
new WearDisplayOffloadMediator(mContextSpy);
// capture the color change listener that is registered
ArgumentCaptor<WallpaperManager.OnColorsChangedListener>
wallpaperColorsChangeListenerCaptor =
ArgumentCaptor.forClass(WallpaperManager.OnColorsChangedListener.class);
verify(mWallpaperManagerMock).addOnColorsChangedListener(
wallpaperColorsChangeListenerCaptor.capture(), any());
WallpaperManager.OnColorsChangedListener wallpaperColorsChangeListener =
wallpaperColorsChangeListenerCaptor.getValue();
assertNotNull(wallpaperColorsChangeListener);
when(mDisplayMock.getState()).thenReturn(Display.STATE_DOZE_SUSPEND);
// invoke the listener as if a color change has occurred
wallpaperColorsChangeListener.onColorsChanged(
new WallpaperColors(Color.valueOf(Color.CYAN), Color.valueOf(Color.DKGRAY),
Color.valueOf(Color.WHITE)), 0);
// verify the screen is woken as a result of the color change
verify(mPowerManagerMock).wakeUp(anyLong(), eq(WAKE_REASON_APPLICATION), anyString());
}
}