• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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