1 /* 2 * Copyright (C) 2020 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.server.power; 18 19 import static org.junit.Assert.assertNotNull; 20 import static org.junit.Assert.assertNull; 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyInt; 23 import static org.mockito.ArgumentMatchers.anyString; 24 import static org.mockito.ArgumentMatchers.eq; 25 import static org.mockito.Mockito.clearInvocations; 26 import static org.mockito.Mockito.never; 27 import static org.mockito.Mockito.spy; 28 import static org.mockito.Mockito.times; 29 import static org.mockito.Mockito.verify; 30 import static org.mockito.Mockito.verifyNoMoreInteractions; 31 import static org.mockito.Mockito.verifyZeroInteractions; 32 import static org.mockito.Mockito.when; 33 34 import android.content.Context; 35 import android.content.res.Resources; 36 import android.hardware.SensorManager; 37 import android.hardware.display.AmbientDisplayConfiguration; 38 import android.os.BatteryStats; 39 import android.os.Handler; 40 import android.os.IWakeLockCallback; 41 import android.os.Looper; 42 import android.os.PowerManager; 43 import android.os.RemoteException; 44 import android.os.ServiceManager; 45 import android.os.VibrationAttributes; 46 import android.os.Vibrator; 47 import android.os.test.TestLooper; 48 import android.provider.Settings; 49 import android.testing.TestableContext; 50 51 import androidx.test.InstrumentationRegistry; 52 53 import com.android.internal.app.IBatteryStats; 54 import com.android.server.LocalServices; 55 import com.android.server.policy.WindowManagerPolicy; 56 import com.android.server.power.batterysaver.BatterySaverStateMachine; 57 import com.android.server.power.feature.PowerManagerFlags; 58 import com.android.server.statusbar.StatusBarManagerInternal; 59 60 import org.junit.Before; 61 import org.junit.Test; 62 import org.mockito.Mock; 63 import org.mockito.MockitoAnnotations; 64 65 import java.util.concurrent.Executor; 66 67 /** 68 * Tests for {@link com.android.server.power.Notifier} 69 */ 70 public class NotifierTest { 71 private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent"; 72 private static final int USER_ID = 0; 73 74 @Mock private BatterySaverStateMachine mBatterySaverStateMachineMock; 75 @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock; 76 @Mock private Notifier mNotifierMock; 77 @Mock private WirelessChargerDetector mWirelessChargerDetectorMock; 78 @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock; 79 @Mock private SystemPropertiesWrapper mSystemPropertiesMock; 80 @Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock; 81 @Mock private Vibrator mVibrator; 82 @Mock private StatusBarManagerInternal mStatusBarManagerInternal; 83 @Mock private WakeLockLog mWakeLockLog; 84 85 @Mock private PowerManagerFlags mPowerManagerFlags; 86 87 private PowerManagerService mService; 88 private Context mContextSpy; 89 private Resources mResourcesSpy; 90 private TestLooper mTestLooper = new TestLooper(); 91 private FakeExecutor mTestExecutor = new FakeExecutor(); 92 private Notifier mNotifier; 93 94 @Before setUp()95 public void setUp() { 96 MockitoAnnotations.initMocks(this); 97 98 LocalServices.removeServiceForTest(StatusBarManagerInternal.class); 99 LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal); 100 101 mContextSpy = spy(new TestableContext(InstrumentationRegistry.getContext())); 102 mResourcesSpy = spy(mContextSpy.getResources()); 103 when(mContextSpy.getResources()).thenReturn(mResourcesSpy); 104 when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn(""); 105 when(mContextSpy.getSystemService(Vibrator.class)).thenReturn(mVibrator); 106 107 mService = new PowerManagerService(mContextSpy, mInjector); 108 } 109 110 @Test testVibrateEnabled_wiredCharging()111 public void testVibrateEnabled_wiredCharging() { 112 createNotifier(); 113 114 // GIVEN the charging vibration is enabled 115 enableChargingVibration(true); 116 117 // WHEN wired charging starts 118 mNotifier.onWiredChargingStarted(USER_ID); 119 mTestLooper.dispatchAll(); 120 mTestExecutor.simulateAsyncExecutionOfLastCommand(); 121 122 // THEN the device vibrates once 123 verify(mVibrator, times(1)).vibrate(anyInt(), any(), any(), any(), 124 any(VibrationAttributes.class)); 125 } 126 127 @Test testVibrateDisabled_wiredCharging()128 public void testVibrateDisabled_wiredCharging() { 129 createNotifier(); 130 131 // GIVEN the charging vibration is disabled 132 enableChargingVibration(false); 133 134 // WHEN wired charging starts 135 mNotifier.onWiredChargingStarted(USER_ID); 136 mTestLooper.dispatchAll(); 137 mTestExecutor.simulateAsyncExecutionOfLastCommand(); 138 139 // THEN the device doesn't vibrate 140 verifyZeroInteractions(mVibrator); 141 } 142 143 @Test testVibrateEnabled_wirelessCharging()144 public void testVibrateEnabled_wirelessCharging() { 145 createNotifier(); 146 147 // GIVEN the charging vibration is enabled 148 enableChargingVibration(true); 149 150 // WHEN wireless charging starts 151 mNotifier.onWirelessChargingStarted(5, USER_ID); 152 mTestLooper.dispatchAll(); 153 mTestExecutor.simulateAsyncExecutionOfLastCommand(); 154 155 // THEN the device vibrates once 156 verify(mVibrator, times(1)).vibrate(anyInt(), any(), any(), any(), 157 any(VibrationAttributes.class)); 158 } 159 160 @Test testVibrateDisabled_wirelessCharging()161 public void testVibrateDisabled_wirelessCharging() { 162 createNotifier(); 163 164 // GIVEN the charging vibration is disabled 165 enableChargingVibration(false); 166 167 // WHEN wireless charging starts 168 mNotifier.onWirelessChargingStarted(5, USER_ID); 169 mTestLooper.dispatchAll(); 170 mTestExecutor.simulateAsyncExecutionOfLastCommand(); 171 172 // THEN the device doesn't vibrate 173 verifyZeroInteractions(mVibrator); 174 } 175 176 @Test testVibrateEnabled_dndOn()177 public void testVibrateEnabled_dndOn() { 178 createNotifier(); 179 180 // GIVEN the charging vibration is enabled but dnd is on 181 enableChargingVibration(true); 182 enableChargingFeedback( 183 /* chargingFeedbackEnabled */ true, 184 /* dndOn */ true); 185 186 // WHEN wired charging starts 187 mNotifier.onWiredChargingStarted(USER_ID); 188 mTestLooper.dispatchAll(); 189 mTestExecutor.simulateAsyncExecutionOfLastCommand(); 190 191 // THEN the device doesn't vibrate 192 verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class)); 193 } 194 195 @Test testWirelessAnimationEnabled()196 public void testWirelessAnimationEnabled() { 197 // GIVEN the wireless charging animation is enabled 198 when(mResourcesSpy.getBoolean( 199 com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim)) 200 .thenReturn(true); 201 createNotifier(); 202 203 // WHEN wireless charging starts 204 mNotifier.onWirelessChargingStarted(5, USER_ID); 205 mTestLooper.dispatchAll(); 206 mTestExecutor.simulateAsyncExecutionOfLastCommand(); 207 208 // THEN the charging animation is triggered 209 verify(mStatusBarManagerInternal, times(1)).showChargingAnimation(5); 210 } 211 212 @Test testWirelessAnimationDisabled()213 public void testWirelessAnimationDisabled() { 214 // GIVEN the wireless charging animation is disabled 215 when(mResourcesSpy.getBoolean( 216 com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim)) 217 .thenReturn(false); 218 createNotifier(); 219 220 // WHEN wireless charging starts 221 mNotifier.onWirelessChargingStarted(5, USER_ID); 222 mTestLooper.dispatchAll(); 223 mTestExecutor.simulateAsyncExecutionOfLastCommand(); 224 225 // THEN the charging animation never gets called 226 verify(mStatusBarManagerInternal, never()).showChargingAnimation(anyInt()); 227 } 228 229 @Test testOnWakeLockListener_RemoteException_NoRethrow()230 public void testOnWakeLockListener_RemoteException_NoRethrow() { 231 when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true); 232 createNotifier(); 233 234 IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() { 235 @Override public void onStateChanged(boolean enabled) throws RemoteException { 236 throw new RemoteException("Just testing"); 237 } 238 }; 239 240 final int uid = 1234; 241 final int pid = 5678; 242 mNotifier.onWakeLockReleased(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag", 243 "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null, 244 exceptingCallback); 245 verifyZeroInteractions(mWakeLockLog); 246 mTestLooper.dispatchAll(); 247 verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, 1); 248 mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag", 249 "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null, 250 exceptingCallback); 251 mNotifier.onWakeLockChanging(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag", 252 "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null, 253 exceptingCallback, 254 PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag", 255 "my.package.name", uid, pid, /* newWorkSource= */ null, /* newHistoryTag= */ null, 256 exceptingCallback); 257 verifyNoMoreInteractions(mWakeLockLog); 258 mTestLooper.dispatchAll(); 259 verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", uid, 260 PowerManager.PARTIAL_WAKE_LOCK, 1); 261 // If we didn't throw, we're good! 262 263 // Test with improveWakelockLatency flag false, hence the wakelock log will run on the same 264 // thread 265 clearInvocations(mWakeLockLog); 266 when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(false); 267 268 mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag", 269 "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null, 270 exceptingCallback); 271 verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", uid, 272 PowerManager.PARTIAL_WAKE_LOCK, -1); 273 274 mNotifier.onWakeLockReleased(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag", 275 "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null, 276 exceptingCallback); 277 verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, -1); 278 } 279 280 private final PowerManagerService.Injector mInjector = new PowerManagerService.Injector() { 281 @Override 282 Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, 283 SuspendBlocker suspendBlocker, WindowManagerPolicy policy, 284 FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector, 285 Executor backgroundExecutor, PowerManagerFlags powerManagerFlags) { 286 return mNotifierMock; 287 } 288 289 @Override 290 SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) { 291 return super.createSuspendBlocker(service, name); 292 } 293 294 @Override 295 BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context) { 296 return mBatterySaverStateMachineMock; 297 } 298 299 @Override 300 PowerManagerService.NativeWrapper createNativeWrapper() { 301 return mNativeWrapperMock; 302 } 303 304 @Override 305 WirelessChargerDetector createWirelessChargerDetector( 306 SensorManager sensorManager, SuspendBlocker suspendBlocker, Handler handler) { 307 return mWirelessChargerDetectorMock; 308 } 309 310 @Override 311 AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) { 312 return mAmbientDisplayConfigurationMock; 313 } 314 315 @Override 316 InattentiveSleepWarningController createInattentiveSleepWarningController() { 317 return mInattentiveSleepWarningControllerMock; 318 } 319 320 @Override 321 public SystemPropertiesWrapper createSystemPropertiesWrapper() { 322 return mSystemPropertiesMock; 323 } 324 325 @Override 326 void invalidateIsInteractiveCaches() { 327 // Avoids an SELinux denial. 328 } 329 }; 330 enableChargingFeedback(boolean chargingFeedbackEnabled, boolean dndOn)331 private void enableChargingFeedback(boolean chargingFeedbackEnabled, boolean dndOn) { 332 // enable/disable charging feedback 333 Settings.Secure.putIntForUser( 334 mContextSpy.getContentResolver(), 335 Settings.Secure.CHARGING_SOUNDS_ENABLED, 336 chargingFeedbackEnabled ? 1 : 0, 337 USER_ID); 338 339 // toggle on/off dnd 340 Settings.Global.putInt( 341 mContextSpy.getContentResolver(), 342 Settings.Global.ZEN_MODE, 343 dndOn ? Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS 344 : Settings.Global.ZEN_MODE_OFF); 345 } 346 enableChargingVibration(boolean enable)347 private void enableChargingVibration(boolean enable) { 348 enableChargingFeedback(true, false); 349 350 Settings.Secure.putIntForUser( 351 mContextSpy.getContentResolver(), 352 Settings.Secure.CHARGING_VIBRATION_ENABLED, 353 enable ? 1 : 0, 354 USER_ID); 355 } 356 createNotifier()357 private void createNotifier() { 358 Notifier.Injector injector = new Notifier.Injector() { 359 @Override 360 public long currentTimeMillis() { 361 return 1; 362 } 363 364 @Override 365 public WakeLockLog getWakeLockLog(Context context) { 366 return mWakeLockLog; 367 } 368 }; 369 370 mNotifier = new Notifier( 371 mTestLooper.getLooper(), 372 mContextSpy, 373 IBatteryStats.Stub.asInterface(ServiceManager.getService( 374 BatteryStats.SERVICE_NAME)), 375 mInjector.createSuspendBlocker(mService, "testBlocker"), 376 null, 377 null, 378 null, 379 mTestExecutor, mPowerManagerFlags, injector); 380 } 381 382 private static class FakeExecutor implements Executor { 383 private Runnable mLastCommand; 384 385 @Override execute(Runnable command)386 public void execute(Runnable command) { 387 assertNull(mLastCommand); 388 assertNotNull(command); 389 mLastCommand = command; 390 } 391 getAndResetLastCommand()392 public Runnable getAndResetLastCommand() { 393 Runnable toReturn = mLastCommand; 394 mLastCommand = null; 395 return toReturn; 396 } 397 simulateAsyncExecutionOfLastCommand()398 public void simulateAsyncExecutionOfLastCommand() { 399 Runnable toRun = getAndResetLastCommand(); 400 if (toRun != null) { 401 toRun.run(); 402 } 403 } 404 } 405 406 } 407