diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 89d24b194b139..bb23bc0e2414a 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -715,6 +715,15 @@ public final class PowerManagerService extends SystemService NativeWrapper createNativeWrapper() { return new NativeWrapper(); } + + WirelessChargerDetector createWirelessChargerDetector( + SensorManager sensorManager, SuspendBlocker suspendBlocker, Handler handler) { + return new WirelessChargerDetector(sensorManager, suspendBlocker, handler); + } + + AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) { + return new AmbientDisplayConfiguration(context); + } } final Constants mConstants; @@ -747,7 +756,7 @@ public final class PowerManagerService extends SystemService mHandlerThread.start(); mHandler = new PowerManagerHandler(mHandlerThread.getLooper()); mConstants = new Constants(mHandler); - mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext); + mAmbientDisplayConfiguration = mInjector.createAmbientDisplayConfiguration(context); mAttentionDetector = new AttentionDetector(this::onUserAttention, mLock); mBatterySavingStats = new BatterySavingStats(mLock); @@ -833,7 +842,7 @@ public final class PowerManagerService extends SystemService mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"), mPolicy); - mWirelessChargerDetector = new WirelessChargerDetector(sensorManager, + mWirelessChargerDetector = mInjector.createWirelessChargerDetector(sensorManager, mInjector.createSuspendBlocker( this, "PowerManagerService.WirelessChargerDetector"), mHandler); @@ -932,7 +941,8 @@ public final class PowerManagerService extends SystemService mContext.registerReceiver(new DockReceiver(), filter, null, mHandler); } - private void readConfigurationLocked() { + @VisibleForTesting + void readConfigurationLocked() { final Resources resources = mContext.getResources(); mDecoupleHalAutoSuspendModeFromDisplayConfig = resources.getBoolean( @@ -3865,7 +3875,8 @@ public final class PowerManagerService extends SystemService return workSource != null ? new WorkSource(workSource) : null; } - private final class BatteryReceiver extends BroadcastReceiver { + @VisibleForTesting + final class BatteryReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { synchronized (mLock) { @@ -3883,7 +3894,8 @@ public final class PowerManagerService extends SystemService } } - private final class UserSwitchedReceiver extends BroadcastReceiver { + @VisibleForTesting + final class UserSwitchedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { synchronized (mLock) { diff --git a/services/core/java/com/android/server/power/WirelessChargerDetector.java b/services/core/java/com/android/server/power/WirelessChargerDetector.java index 18e5ce465df6f..e8e9c676135d8 100644 --- a/services/core/java/com/android/server/power/WirelessChargerDetector.java +++ b/services/core/java/com/android/server/power/WirelessChargerDetector.java @@ -28,6 +28,8 @@ import android.util.Slog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.VisibleForTesting; + import java.io.PrintWriter; /** @@ -70,7 +72,8 @@ import java.io.PrintWriter; * sensor to detect this case. *

*/ -final class WirelessChargerDetector { +@VisibleForTesting +public class WirelessChargerDetector { private static final String TAG = "WirelessChargerDetector"; private static final boolean DEBUG = false; diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index 911c4a2f41221..1bda412f2f89d 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -18,44 +18,67 @@ package com.android.server.power; import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP; import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE; +import static android.os.PowerManagerInternal.WAKEFULNESS_DOZING; +import static android.os.PowerManagerInternal.WAKEFULNESS_DREAMING; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManagerInternal; import android.attention.AttentionManagerInternal; import android.content.Context; +import android.content.ContextWrapper; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources; +import android.hardware.SensorManager; +import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; +import android.os.BatteryManager; import android.os.BatteryManagerInternal; import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.PowerManager; import android.os.PowerSaveState; import android.os.SystemClock; import android.os.SystemProperties; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.MediumTest; -import android.test.suitebuilder.annotation.SmallTest; +import android.os.UserHandle; +import android.provider.Settings; + +import androidx.test.InstrumentationRegistry; import com.android.internal.app.IBatteryStats; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.lights.LightsManager; import com.android.server.policy.WindowManagerPolicy; +import com.android.server.power.PowerManagerService.BatteryReceiver; import com.android.server.power.PowerManagerService.Injector; import com.android.server.power.PowerManagerService.NativeWrapper; +import com.android.server.power.PowerManagerService.UserSwitchedReceiver; import com.android.server.power.batterysaver.BatterySaverPolicy; import com.android.server.power.batterysaver.BatterySavingStats; -import org.junit.Rule; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -65,29 +88,54 @@ import java.util.Map; /** * Tests for {@link com.android.server.power.PowerManagerService} */ -public class PowerManagerServiceTest extends AndroidTestCase { +public class PowerManagerServiceTest { private static final float PRECISION = 0.001f; private static final float BRIGHTNESS_FACTOR = 0.7f; private static final boolean BATTERY_SAVER_ENABLED = true; private static final String TEST_LAST_REBOOT_PROPERTY = "test.sys.boot.reason"; - private @Mock BatterySaverPolicy mBatterySaverPolicyMock; - private @Mock LightsManager mLightsManagerMock; - private @Mock DisplayManagerInternal mDisplayManagerInternalMock; - private @Mock BatteryManagerInternal mBatteryManagerInternalMock; - private @Mock ActivityManagerInternal mActivityManagerInternalMock; - private @Mock AttentionManagerInternal mAttentionManagerInternalMock; - private @Mock PowerManagerService.NativeWrapper mNativeWrapperMock; - private @Mock Notifier mNotifierMock; + @Mock private BatterySaverPolicy mBatterySaverPolicyMock; + @Mock private LightsManager mLightsManagerMock; + @Mock private DisplayManagerInternal mDisplayManagerInternalMock; + @Mock private BatteryManagerInternal mBatteryManagerInternalMock; + @Mock private ActivityManagerInternal mActivityManagerInternalMock; + @Mock private AttentionManagerInternal mAttentionManagerInternalMock; + @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock; + @Mock private Notifier mNotifierMock; + @Mock private WirelessChargerDetector mWirelessChargerDetectorMock; + @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock; + private PowerManagerService mService; private PowerSaveState mPowerSaveState; private DisplayPowerRequest mDisplayPowerRequest; + private ContextWrapper mContextSpy; + private BatteryReceiver mBatteryReceiver; + private UserSwitchedReceiver mUserSwitchedReceiver; + private Resources mResourcesSpy; + private class IntentFilterMatcher implements ArgumentMatcher { + private final IntentFilter mFilter; + IntentFilterMatcher(IntentFilter filter) { + mFilter = filter; + } - @Rule + @Override + public boolean matches(IntentFilter other) { + if (other.countActions() != mFilter.countActions()) { + return false; + } + for (int i = 0; i < mFilter.countActions(); i++) { + if (!mFilter.getAction(i).equals(other.getAction(i))) { + return false; + } + } + return true; + } + } + + @Before public void setUp() throws Exception { - super.setUp(); MockitoAnnotations.initMocks(this); mPowerSaveState = new PowerSaveState.Builder() @@ -105,33 +153,54 @@ public class PowerManagerServiceTest extends AndroidTestCase { addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock); addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock); - mService = new PowerManagerService(getContext(), new Injector() { + mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); + mResourcesSpy = spy(mContextSpy.getResources()); + when(mContextSpy.getResources()).thenReturn(mResourcesSpy); + + mService = new PowerManagerService(mContextSpy, new Injector() { + @Override Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy) { return mNotifierMock; } + @Override SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) { return mock(SuspendBlocker.class); } + @Override BatterySaverPolicy createBatterySaverPolicy( Object lock, Context context, BatterySavingStats batterySavingStats) { return mBatterySaverPolicyMock; } + @Override NativeWrapper createNativeWrapper() { return mNativeWrapperMock; } + + @Override + WirelessChargerDetector createWirelessChargerDetector( + SensorManager sensorManager, SuspendBlocker suspendBlocker, Handler handler) { + return mWirelessChargerDetectorMock; + } + + @Override + AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) { + return mAmbientDisplayConfigurationMock; + } }); } - @Override + @After public void tearDown() throws Exception { LocalServices.removeServiceForTest(LightsManager.class); LocalServices.removeServiceForTest(DisplayManagerInternal.class); LocalServices.removeServiceForTest(BatteryManagerInternal.class); LocalServices.removeServiceForTest(ActivityManagerInternal.class); + Settings.Global.putInt( + mContextSpy.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0); } /** @@ -142,7 +211,56 @@ public class PowerManagerServiceTest extends AndroidTestCase { LocalServices.addService(clazz, mock); } - @SmallTest + private void startSystem() throws Exception { + mService.systemReady(null); + + // Grab the BatteryReceiver + ArgumentCaptor batCaptor = ArgumentCaptor.forClass(BatteryReceiver.class); + IntentFilter batFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); + batFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + verify(mContextSpy).registerReceiver(batCaptor.capture(), + argThat(new IntentFilterMatcher(batFilter)), isNull(), isA(Handler.class)); + mBatteryReceiver = batCaptor.getValue(); + + // Grab the UserSwitchedReceiver + ArgumentCaptor userSwitchedCaptor = + ArgumentCaptor.forClass(UserSwitchedReceiver.class); + IntentFilter usFilter = new IntentFilter(Intent.ACTION_USER_SWITCHED); + verify(mContextSpy).registerReceiver(userSwitchedCaptor.capture(), + argThat(new IntentFilterMatcher(usFilter)), isNull(), isA(Handler.class)); + mUserSwitchedReceiver = userSwitchedCaptor.getValue(); + + mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + } + + private void forceSleep() { + mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_APPLICATION, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); + } + + private void forceDream() { + mService.getBinderServiceInstance().nap(SystemClock.uptimeMillis()); + } + + private void forceAwake() { + mService.getBinderServiceInstance().wakeUp(SystemClock.uptimeMillis(), + PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name"); + } + + private void forceDozing() { + mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0); + } + + private void setPluggedIn(boolean isPluggedIn) { + // Set the callback to return the new state + when(mBatteryManagerInternalMock.isPowered(BatteryManager.BATTERY_PLUGGED_ANY)) + .thenReturn(isPluggedIn); + // Trigger PowerManager to reread the plug-in state + mBatteryReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_BATTERY_CHANGED)); + } + + @Test public void testUpdatePowerScreenPolicy_UpdateDisplayPowerRequest() { mService.updatePowerRequestFromBatterySaverPolicy(mDisplayPowerRequest); assertThat(mDisplayPowerRequest.lowPowerMode).isEqualTo(BATTERY_SAVER_ENABLED); @@ -150,7 +268,7 @@ public class PowerManagerServiceTest extends AndroidTestCase { .isWithin(PRECISION).of(BRIGHTNESS_FACTOR); } - @SmallTest + @Test public void testGetLastShutdownReasonInternal() { SystemProperties.set(TEST_LAST_REBOOT_PROPERTY, "shutdown,thermal"); int reason = mService.getLastShutdownReasonInternal(TEST_LAST_REBOOT_PROPERTY); @@ -158,7 +276,7 @@ public class PowerManagerServiceTest extends AndroidTestCase { assertThat(reason).isEqualTo(PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN); } - @SmallTest + @Test public void testGetDesiredScreenPolicy_WithVR() throws Exception { // Brighten up the screen mService.setWakefulnessLocked(WAKEFULNESS_AWAKE, PowerManager.WAKE_REASON_UNKNOWN, 0); @@ -187,42 +305,171 @@ public class PowerManagerServiceTest extends AndroidTestCase { DisplayPowerRequest.POLICY_BRIGHT); } - @SmallTest + @Test public void testWakefulnessAwake_InitialValue() throws Exception { assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE); } - @SmallTest + @Test public void testWakefulnessSleep_NoDozeSleepFlag() throws Exception { // Start with AWAKE state + startSystem(); assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE); - mService.systemReady(null); - mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); - - // Take a nap with a flag. + // Take a nap and verify. mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(), - PowerManager.GO_TO_SLEEP_REASON_APPLICATION, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); - + PowerManager.GO_TO_SLEEP_REASON_APPLICATION, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); } - @MediumTest + @Test + public void testWakefulnessAwake_AcquireCausesWakeup() throws Exception { + startSystem(); + forceSleep(); + + IBinder token = new Binder(); + String tag = "acq_causes_wakeup"; + String packageName = "pkg.name"; + + // First, ensure that a normal full wake lock does not cause a wakeup + int flags = PowerManager.FULL_WAKE_LOCK; + mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, + null /* workSource */, null /* historyTag */); + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); + mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */); + + // Ensure that the flag does *NOT* work with a partial wake lock. + flags = PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; + mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, + null /* workSource */, null /* historyTag */); + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); + mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */); + + // Verify that flag forces a wakeup when paired to a FULL_WAKE_LOCK + flags = PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP; + mService.getBinderServiceInstance().acquireWakeLock(token, flags, tag, packageName, + null /* workSource */, null /* historyTag */); + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE); + mService.getBinderServiceInstance().releaseWakeLock(token, 0 /* flags */); + } + + @Test + public void testWakefulnessAwake_IPowerManagerWakeUp() throws Exception { + startSystem(); + forceSleep(); + mService.getBinderServiceInstance().wakeUp(SystemClock.uptimeMillis(), + PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name"); + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE); + } + + /** + * Tests a series of variants that control whether a device wakes-up when it is plugged in + * or docked. + */ + @Test + public void testWakefulnessAwake_ShouldWakeUpWhenPluggedIn() throws Exception { + boolean powerState; + startSystem(); + forceSleep(); + + // Test 1: + // Set config to prevent it wake up, test, verify, reset config value. + when(mResourcesSpy.getBoolean(com.android.internal.R.bool.config_unplugTurnsOnScreen)) + .thenReturn(false); + mService.readConfigurationLocked(); + setPluggedIn(true); + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); + when(mResourcesSpy.getBoolean(com.android.internal.R.bool.config_unplugTurnsOnScreen)) + .thenReturn(true); + mService.readConfigurationLocked(); + + // Test 2: + // Turn the power off, sleep, then plug into a wireless charger. + // Verify that we do not wake up if the phone is being plugged into a wireless charger. + setPluggedIn(false); + forceSleep(); + when(mBatteryManagerInternalMock.getPlugType()) + .thenReturn(BatteryManager.BATTERY_PLUGGED_WIRELESS); + when(mWirelessChargerDetectorMock.update(true /* isPowered */, + BatteryManager.BATTERY_PLUGGED_WIRELESS)).thenReturn(false); + setPluggedIn(true); + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); + + // Test 3: + // Do not wake up if the phone is being REMOVED from a wireless charger + when(mBatteryManagerInternalMock.getPlugType()).thenReturn(0); + setPluggedIn(false); + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); + + // Test 4: + // Do not wake if we are dreaming. + forceAwake(); // Needs to be awake first before it can dream. + forceDream(); + setPluggedIn(true); + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_DREAMING); + forceSleep(); + + // Test 5: + // Don't wake if the device is configured not to wake up in theater mode (and theater + // mode is enabled). + Settings.Global.putInt( + mContextSpy.getContentResolver(), Settings.Global.THEATER_MODE_ON, 1); + mUserSwitchedReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_USER_SWITCHED)); + when(mResourcesSpy.getBoolean( + com.android.internal.R.bool.config_allowTheaterModeWakeFromUnplug)) + .thenReturn(false); + setPluggedIn(false); + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); + Settings.Global.putInt( + mContextSpy.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0); + mUserSwitchedReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_USER_SWITCHED)); + + // Test 6: + // Don't wake up if we are Dozing away and always-on is enabled. + when(mAmbientDisplayConfigurationMock.alwaysOnEnabled(UserHandle.USER_CURRENT)) + .thenReturn(true); + mUserSwitchedReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_USER_SWITCHED)); + forceAwake(); + forceDozing(); + setPluggedIn(true); + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_DOZING); + + // Test 7: + // Finally, take away all the factors above and ensure the device wakes up! + forceAwake(); + forceSleep(); + setPluggedIn(false); + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE); + } + + @Test + public void testWakefulnessDoze_goToSleep() throws Exception { + // Start with AWAKE state + startSystem(); + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_AWAKE); + + // Take a nap and verify. + mService.getBinderServiceInstance().goToSleep(SystemClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_APPLICATION, 0); + assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_DOZING); + } + + @Test public void testWasDeviceIdleFor_true() { int interval = 1000; mService.onUserActivity(); - SystemClock.sleep(interval); + SystemClock.sleep(interval + 1 /* just a little more */); assertThat(mService.wasDeviceIdleForInternal(interval)).isTrue(); } - @SmallTest + @Test public void testWasDeviceIdleFor_false() { int interval = 1000; mService.onUserActivity(); assertThat(mService.wasDeviceIdleForInternal(interval)).isFalse(); } - @SmallTest + @Test public void testForceSuspend_putsDeviceToSleep() { mService.systemReady(null); mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); @@ -244,11 +491,11 @@ public class PowerManagerServiceTest extends AndroidTestCase { assertThat(mService.getWakefulness()).isEqualTo(WAKEFULNESS_ASLEEP); } - @SmallTest + @Test public void testForceSuspend_pakeLocksDisabled() { final String tag = "TestWakelockTag_098213"; final int flags = PowerManager.PARTIAL_WAKE_LOCK; - final String pkg = getContext().getOpPackageName(); + final String pkg = mContextSpy.getOpPackageName(); // Set up the Notification mock to keep track of the wakelocks that are currently // active or disabled. We'll use this to verify that wakelocks are disabled when @@ -292,7 +539,7 @@ public class PowerManagerServiceTest extends AndroidTestCase { } - @SmallTest + @Test public void testForceSuspend_forceSuspendFailurePropogated() { when(mNativeWrapperMock.nativeForceSuspend()).thenReturn(false); assertThat(mService.getBinderServiceInstance().forceSuspend()).isFalse();