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