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