1 /*
2  * Copyright (C) 2008 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 android.os;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.fail;
22 import static org.mockito.Mockito.reset;
23 import static org.mockito.Mockito.timeout;
24 import static org.mockito.Mockito.verify;
25 
26 import android.content.Context;
27 import android.support.test.uiautomator.UiDevice;
28 import android.test.AndroidTestCase;
29 
30 import androidx.test.InstrumentationRegistry;
31 import androidx.test.filters.SmallTest;
32 
33 import org.junit.After;
34 import org.junit.Test;
35 import org.mockito.Mock;
36 import org.mockito.MockitoAnnotations;
37 
38 import java.util.concurrent.Executor;
39 import java.util.concurrent.Executors;
40 
41 public class PowerManagerTest extends AndroidTestCase {
42 
43     private static final String TAG = "PowerManagerTest";
44     private PowerManager mPm;
45     private UiDevice mUiDevice;
46     private Executor mExec = Executors.newSingleThreadExecutor();
47     @Mock
48     private PowerManager.OnThermalStatusChangedListener mListener1;
49     @Mock
50     private PowerManager.OnThermalStatusChangedListener mListener2;
51     private static final long CALLBACK_TIMEOUT_MILLI_SEC = 5000;
nativeObtainPowerSaveStateParcel(boolean batterySaverEnabled, boolean globalBatterySaverEnabled, int locationMode, int soundTriggerMode, float brightnessFactor)52     private native Parcel nativeObtainPowerSaveStateParcel(boolean batterySaverEnabled,
53             boolean globalBatterySaverEnabled, int locationMode, int soundTriggerMode,
54             float brightnessFactor);
nativeUnparcelAndVerifyPowerSaveState(Parcel parcel, boolean batterySaverEnabled, boolean globalBatterySaverEnabled, int locationMode, int soundTriggerMode, float brightnessFactor)55     private native void nativeUnparcelAndVerifyPowerSaveState(Parcel parcel,
56             boolean batterySaverEnabled, boolean globalBatterySaverEnabled,
57             int locationMode, int soundTriggerMode, float brightnessFactor);
nativeObtainBSPConfigParcel(BatterySaverPolicyConfig bs, String[] keys, String[] values)58     private native Parcel nativeObtainBSPConfigParcel(BatterySaverPolicyConfig bs,
59             String[] keys, String[] values);
nativeUnparcelAndVerifyBSPConfig(Parcel parcel, BatterySaverPolicyConfig bs, String[] keys, String[] values)60     private native void nativeUnparcelAndVerifyBSPConfig(Parcel parcel, BatterySaverPolicyConfig bs,
61             String[] keys, String[] values);
62 
63     static {
64         System.loadLibrary("powermanagertest_jni");
65     }
66 
67     /**
68      * Setup any common data for the upcoming tests.
69      */
70     @Override
setUp()71     public void setUp() throws Exception {
72         super.setUp();
73         MockitoAnnotations.initMocks(this);
74         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
75         mPm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
76         mUiDevice.executeShellCommand("cmd thermalservice override-status 0");
77     }
78 
79     /**
80      * Reset data for the upcoming tests.
81      */
82     @After
tearDown()83     public void tearDown() throws Exception {
84         mUiDevice.executeShellCommand("cmd thermalservice reset");
85     }
86 
87     /**
88      * Confirm that the setup is good.
89      *
90      * @throws Exception
91      */
92     @SmallTest
testPreconditions()93     public void testPreconditions() throws Exception {
94         assertNotNull(mPm);
95     }
96 
97     /**
98      * Confirm that we can create functional wakelocks.
99      *
100      * @throws Exception
101      */
102     @SmallTest
testNewWakeLock()103     public void testNewWakeLock() throws Exception {
104         PowerManager.WakeLock wl = mPm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "FULL_WAKE_LOCK");
105         doTestWakeLock(wl);
106 
107         wl = mPm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "SCREEN_BRIGHT_WAKE_LOCK");
108         doTestWakeLock(wl);
109 
110         wl = mPm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "SCREEN_DIM_WAKE_LOCK");
111         doTestWakeLock(wl);
112 
113         wl = mPm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "PARTIAL_WAKE_LOCK");
114         doTestWakeLock(wl);
115 
116         // TODO: Some sort of functional test (maybe not in the unit test here?)
117         // that confirms that things are really happening e.g. screen power, keyboard power.
118     }
119 
120     /**
121      * Confirm that we can't create dysfunctional wakelocks.
122      *
123      * @throws Exception
124      */
125     @SmallTest
testBadNewWakeLock()126     public void testBadNewWakeLock() throws Exception {
127         final int badFlags = PowerManager.SCREEN_BRIGHT_WAKE_LOCK
128                             | PowerManager.SCREEN_DIM_WAKE_LOCK;
129         // wrap in try because we want the error here
130         try {
131             PowerManager.WakeLock wl = mPm.newWakeLock(badFlags, "foo");
132         } catch (IllegalArgumentException e) {
133             return;
134         }
135         fail("Bad WakeLock flag was not caught.");
136     }
137 
138     /**
139      * Ensure that we can have work sources with work chains when uid is not set directly on work
140      * source, and that this doesn't crash system server.
141      *
142      * @throws Exception
143      */
144     @SmallTest
testWakeLockWithWorkChains()145     public void testWakeLockWithWorkChains() throws Exception {
146         PowerManager.WakeLock wakeLock = mPm.newWakeLock(
147                 PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
148                 "TEST_LOCK");
149         WorkSource workSource = new WorkSource();
150         WorkSource.WorkChain workChain = workSource.createWorkChain();
151         workChain.addNode(1000, "test");
152         wakeLock.setWorkSource(workSource);
153 
154         doTestWakeLock(wakeLock);
155     }
156 
157     /**
158      * Apply a few tests to a wakelock to make sure it's healthy.
159      *
160      * @param wl The wakelock to be tested.
161      */
doTestWakeLock(PowerManager.WakeLock wl)162     private void doTestWakeLock(PowerManager.WakeLock wl) {
163         // First try simple acquire/release
164         wl.acquire();
165         assertTrue(wl.isHeld());
166         wl.release();
167         assertFalse(wl.isHeld());
168 
169         // Try ref-counted acquire/release
170         wl.setReferenceCounted(true);
171         wl.acquire();
172         assertTrue(wl.isHeld());
173         wl.acquire();
174         assertTrue(wl.isHeld());
175         wl.release();
176         assertTrue(wl.isHeld());
177         wl.release();
178         assertFalse(wl.isHeld());
179 
180         // Try non-ref-counted
181         wl.setReferenceCounted(false);
182         wl.acquire();
183         assertTrue(wl.isHeld());
184         wl.acquire();
185         assertTrue(wl.isHeld());
186         wl.release();
187         assertFalse(wl.isHeld());
188 
189         // TODO: Threaded test (needs handler) to make sure timed wakelocks work too
190     }
191 
192     /**
193      * Confirm that we can get thermal status.
194      *
195      * @throws Exception
196      */
197     @Test
testGetThermalStatus()198     public void testGetThermalStatus() throws Exception {
199         int status = 0;
200         assertEquals(status, mPm.getCurrentThermalStatus());
201         status = 3;
202         mUiDevice.executeShellCommand("cmd thermalservice override-status "
203                 + Integer.toString(status));
204         assertEquals(status, mPm.getCurrentThermalStatus());
205     }
206 
207     /**
208      * Confirm that we can add/remove thermal status listener.
209      *
210      * @throws Exception
211      */
212     @Test
testThermalStatusCallback()213     public void testThermalStatusCallback() throws Exception {
214         // Initial override status is THERMAL_STATUS_NONE
215         int status = PowerManager.THERMAL_STATUS_NONE;
216         // Add listener1
217         mPm.addThermalStatusListener(mExec, mListener1);
218         verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
219                 .times(1)).onThermalStatusChanged(status);
220         reset(mListener1);
221         status = PowerManager.THERMAL_STATUS_SEVERE;
222         mUiDevice.executeShellCommand("cmd thermalservice override-status "
223                 + Integer.toString(status));
224         verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
225                 .times(1)).onThermalStatusChanged(status);
226         reset(mListener1);
227         // Add listener1 again
228         try {
229             mPm.addThermalStatusListener(mListener1);
230             fail("Expected exception not thrown");
231         } catch (IllegalArgumentException expectedException) {
232         }
233         // Add listener2 on main thread.
234         mPm.addThermalStatusListener(mListener2);
235         verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
236             .times(1)).onThermalStatusChanged(status);
237         reset(mListener2);
238         status = PowerManager.THERMAL_STATUS_MODERATE;
239         mUiDevice.executeShellCommand("cmd thermalservice override-status "
240                 + Integer.toString(status));
241         verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
242                 .times(1)).onThermalStatusChanged(status);
243         verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
244                 .times(1)).onThermalStatusChanged(status);
245         reset(mListener1);
246         reset(mListener2);
247         // Remove listener1
248         mPm.removeThermalStatusListener(mListener1);
249         // Remove listener1 again
250         try {
251             mPm.removeThermalStatusListener(mListener1);
252             fail("Expected exception not thrown");
253         } catch (IllegalArgumentException expectedException) {
254         }
255         status = PowerManager.THERMAL_STATUS_LIGHT;
256         mUiDevice.executeShellCommand("cmd thermalservice override-status "
257                 + Integer.toString(status));
258         verify(mListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
259                 .times(0)).onThermalStatusChanged(status);
260         verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
261                 .times(1)).onThermalStatusChanged(status);
262     }
263 
264     @Test
testGetThermalHeadroom()265     public void testGetThermalHeadroom() throws Exception {
266         float headroom = mPm.getThermalHeadroom(0);
267         // If the device doesn't support thermal headroom, return early
268         if (Float.isNaN(headroom)) {
269             return;
270         }
271         assertTrue("Expected non-negative headroom", headroom >= 0.0f);
272         assertTrue("Expected reasonably small headroom", headroom < 10.0f);
273 
274         // Call again immediately to ensure rate limiting works
275         headroom = mPm.getThermalHeadroom(0);
276         assertTrue("Expected NaN because of rate limiting", Float.isNaN(headroom));
277 
278         // Sleep for a second before attempting to call again so as to not get rate limited
279         Thread.sleep(1000);
280         headroom = mPm.getThermalHeadroom(5);
281         assertFalse("Expected data to still be available", Float.isNaN(headroom));
282         assertTrue("Expected non-negative headroom", headroom >= 0.0f);
283         assertTrue("Expected reasonably small headroom", headroom < 10.0f);
284     }
285 
286     @Test
testUserspaceRebootNotSupported_throwsUnsupportedOperationException()287     public void testUserspaceRebootNotSupported_throwsUnsupportedOperationException() {
288         // Can't use assumption framework with AndroidTestCase :(
289         if (mPm.isRebootingUserspaceSupported()) {
290             return;
291         }
292         try {
293             mPm.reboot(PowerManager.REBOOT_USERSPACE);
294             fail("UnsupportedOperationException not thrown");
295         } catch (UnsupportedOperationException expected) {
296         }
297     }
298 
299     /**
300      * Helper function to obtain a PowerSaveState as parcel from native, with
301      * specified parameters, and verify the PowerSaveState object created from the parcel.
302      */
unparcelPowerSaveStateFromNativeAndVerify(boolean batterySaverEnabled, boolean globalBatterySaverEnabled, int locationMode, int soundTriggerMode, float brightnessFactor)303     private void unparcelPowerSaveStateFromNativeAndVerify(boolean batterySaverEnabled,
304             boolean globalBatterySaverEnabled, int locationMode, int soundTriggerMode,
305             float brightnessFactor) {
306         // Obtain PowerSaveState as parcel from native, with parameters.
307         Parcel psParcel = nativeObtainPowerSaveStateParcel(batterySaverEnabled,
308                  globalBatterySaverEnabled, locationMode, soundTriggerMode, brightnessFactor);
309         // Verify the parcel.
310         PowerSaveState ps = PowerSaveState.CREATOR.createFromParcel(psParcel);
311         assertEquals(ps.batterySaverEnabled, batterySaverEnabled);
312         assertEquals(ps.globalBatterySaverEnabled, globalBatterySaverEnabled);
313         assertEquals(ps.locationMode, locationMode);
314         assertEquals(ps.soundTriggerMode, soundTriggerMode);
315         assertEquals(ps.brightnessFactor, brightnessFactor, 0.01f);
316     }
317 
318     /**
319      * Helper function to send a PowerSaveState as parcel to native, with
320      * specified parameters. Native will verify the PowerSaveState in native is expected.
321      */
parcelPowerSaveStateToNativeAndVerify(boolean batterySaverEnabled, boolean globalBatterySaverEnabled, int locationMode, int soundTriggerMode, float brightnessFactor)322     private void parcelPowerSaveStateToNativeAndVerify(boolean batterySaverEnabled,
323             boolean globalBatterySaverEnabled, int locationMode, int soundTriggerMode,
324             float brightnessFactor) {
325         Parcel psParcel = Parcel.obtain();
326         // PowerSaveState API blocks Builder.build(), generate a parcel instead of object.
327         PowerSaveState ps = new PowerSaveState.Builder()
328                 .setBatterySaverEnabled(batterySaverEnabled)
329                 .setGlobalBatterySaverEnabled(globalBatterySaverEnabled)
330                 .setLocationMode(locationMode)
331                 .setBrightnessFactor(brightnessFactor).build();
332         ps.writeToParcel(psParcel, 0 /* flags */);
333         psParcel.setDataPosition(0);
334         //Set the PowerSaveState as parcel to native and verify in native space.
335         nativeUnparcelAndVerifyPowerSaveState(psParcel, batterySaverEnabled,
336                 globalBatterySaverEnabled, locationMode, soundTriggerMode, brightnessFactor);
337     }
338 
339     /**
340      * Helper function to obtain a BatterySaverPolicyConfig as parcel from native, with
341      * specified parameters, and verify the BatterySaverPolicyConfig object created from the parcel.
342      */
unparcelBatterySaverPolicyFromNativeAndVerify(BatterySaverPolicyConfig bsIn)343     private void unparcelBatterySaverPolicyFromNativeAndVerify(BatterySaverPolicyConfig bsIn) {
344         // Obtain BatterySaverPolicyConfig as parcel from native, with parameters.
345         String[] keys = bsIn.getDeviceSpecificSettings().keySet().toArray(
346                     new String[bsIn.getDeviceSpecificSettings().keySet().size()]);
347         String[] values = bsIn.getDeviceSpecificSettings().values().toArray(
348                     new String[bsIn.getDeviceSpecificSettings().values().size()]);
349         Parcel bsParcel = nativeObtainBSPConfigParcel(bsIn, keys, values);
350         BatterySaverPolicyConfig bsOut =
351                 BatterySaverPolicyConfig.CREATOR.createFromParcel(bsParcel);
352         assertEquals(bsIn.toString(), bsOut.toString());
353     }
354 
355     /**
356      * Helper function to send a BatterySaverPolicyConfig as parcel to native, with
357      * specified parameters.
358      * Native will verify BatterySaverPolicyConfig from native is expected.
359      */
parcelBatterySaverPolicyConfigToNativeAndVerify(BatterySaverPolicyConfig bsIn)360     private void parcelBatterySaverPolicyConfigToNativeAndVerify(BatterySaverPolicyConfig bsIn) {
361         Parcel bsParcel = Parcel.obtain();
362         bsIn.writeToParcel(bsParcel, 0 /* flags */);
363         bsParcel.setDataPosition(0);
364         // Set the BatterySaverPolicyConfig as parcel to native.
365         String[] keys = bsIn.getDeviceSpecificSettings().keySet().toArray(
366                     new String[bsIn.getDeviceSpecificSettings().keySet().size()]);
367         String[] values = bsIn.getDeviceSpecificSettings().values().toArray(
368                     new String[bsIn.getDeviceSpecificSettings().values().size()]);
369         // Set the BatterySaverPolicyConfig as parcel to native and verify in native space.
370         nativeUnparcelAndVerifyBSPConfig(bsParcel, bsIn, keys, values);
371     }
372 
373     /**
374      * Confirm that we can pass PowerSaveState from native to Java.
375      *
376      * @throws Exception
377      */
378     @Test
testPowerSaveStateNativeToJava()379     public void testPowerSaveStateNativeToJava() {
380         unparcelPowerSaveStateFromNativeAndVerify(false /* batterySaverEnabled */,
381                 false /* globalBatterySaverEnabled */,
382                 PowerManager.LOCATION_MODE_FOREGROUND_ONLY,
383                 PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY,
384                 0.3f /* brightnessFactor */);
385         unparcelPowerSaveStateFromNativeAndVerify(true /* batterySaverEnabled */,
386                 true  /* globalBatterySaverEnabled */,
387                 PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF,
388                 PowerManager.SOUND_TRIGGER_MODE_ALL_DISABLED,
389                 0.5f /* brightnessFactor */);
390     }
391 
392     /**
393      * Confirm that we can pass PowerSaveState from Java to native.
394      *
395      * @throws Exception
396      */
397     @Test
testSetPowerSaveStateJavaToNative()398     public void testSetPowerSaveStateJavaToNative() {
399         parcelPowerSaveStateToNativeAndVerify(false /* batterySaverEnabled */,
400                 false /* globalBatterySaverEnabled */,
401                 PowerManager.LOCATION_MODE_FOREGROUND_ONLY,
402                 PowerManager.SOUND_TRIGGER_MODE_CRITICAL_ONLY,
403                 0.3f /* brightnessFactor */);
404         parcelPowerSaveStateToNativeAndVerify(true /* batterySaverEnabled */,
405                 true  /* globalBatterySaverEnabled */,
406                 PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF,
407                 PowerManager.SOUND_TRIGGER_MODE_ALL_DISABLED,
408                 0.5f /* brightnessFactor */);
409     }
410 
411     /**
412      * Confirm that we can pass BatterySaverPolicyConfig from native to Java.
413      *
414      * @throws Exception
415      */
416     @Test
testBatterySaverPolicyConfigNativeToJava()417     public void testBatterySaverPolicyConfigNativeToJava() {
418         BatterySaverPolicyConfig bs1 = new BatterySaverPolicyConfig.Builder()
419                 .setAdjustBrightnessFactor(0.55f)
420                 .setAdvertiseIsEnabled(true)
421                 .setDeferFullBackup(true)
422                 .setForceBackgroundCheck(true)
423                 .setLocationMode(PowerManager.LOCATION_MODE_FOREGROUND_ONLY).build();
424         BatterySaverPolicyConfig bs2 = new BatterySaverPolicyConfig.Builder()
425                 .setAdjustBrightnessFactor(0.55f)
426                 .setAdvertiseIsEnabled(true)
427                 .setLocationMode(PowerManager.LOCATION_MODE_FOREGROUND_ONLY)
428                 .addDeviceSpecificSetting("Key1" /* key */, "Value1" /* value */)
429                 .addDeviceSpecificSetting("Key2" /* key */, "Value2" /* value */)
430                 .setDeferFullBackup(true).build();
431 
432         unparcelBatterySaverPolicyFromNativeAndVerify(bs1);
433         unparcelBatterySaverPolicyFromNativeAndVerify(bs2);
434     }
435 
436     /**
437      * Confirm that we can pass BatterySaverPolicyConfig from Java to native.
438      *
439      * @throws Exception
440      */
441     @Test
testBatterySaverPolicyConfigFromJavaToNative()442     public void testBatterySaverPolicyConfigFromJavaToNative() {
443         BatterySaverPolicyConfig bs1 = new BatterySaverPolicyConfig.Builder()
444                 .setAdjustBrightnessFactor(0.55f)
445                 .setAdvertiseIsEnabled(true)
446                 .setDeferFullBackup(true).build();
447         BatterySaverPolicyConfig bs2 = new BatterySaverPolicyConfig.Builder()
448                 .setAdjustBrightnessFactor(0.55f)
449                 .setAdvertiseIsEnabled(true)
450                 .addDeviceSpecificSetting("Key1" /* key */, "Value1" /* value */)
451                 .addDeviceSpecificSetting("Key2" /* key */, "Value2" /* value */)
452                 .setDeferFullBackup(true).build();
453         parcelBatterySaverPolicyConfigToNativeAndVerify(bs1);
454         parcelBatterySaverPolicyConfigToNativeAndVerify(bs2);
455     }
456 
457 }
458