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