1 /*
2  * Copyright (C) 2009 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.cts;
18 
19 import static android.os.Flags.batterySaverSupportedCheckApi;
20 
21 import static com.android.compatibility.common.util.TestUtils.waitUntil;
22 
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.os.Flags;
28 import android.os.PowerManager;
29 import android.os.PowerManager.WakeLock;
30 import android.platform.test.annotations.AppModeFull;
31 import android.provider.Settings.Global;
32 import android.test.AndroidTestCase;
33 
34 import androidx.test.filters.LargeTest;
35 
36 import com.android.compatibility.common.util.BatteryUtils;
37 import com.android.compatibility.common.util.CallbackAsserter;
38 import com.android.compatibility.common.util.SystemUtil;
39 
40 import org.junit.After;
41 import org.junit.Before;
42 
43 import java.time.Duration;
44 
45 @AppModeFull(reason = "Instant Apps don't have the WRITE_SECURE_SETTINGS permission "
46         + "required in tearDown for Global#putInt")
47 public class PowerManagerTest extends AndroidTestCase {
48     private static final String TAG = "PowerManagerTest";
49     public static final long TIME = 3000;
50     public static final int MORE_TIME = 300;
51     private static final int BROADCAST_TIMEOUT_SECONDS = 70;
52     private static final Duration LONG_DISCHARGE_DURATION = Duration.ofMillis(2000);
53     private static final Duration SHORT_DISCHARGE_DURATION = Duration.ofMillis(1000);
54 
55     private int mInitialPowerSaverMode;
56     private int mInitialDynamicPowerSavingsEnabled;
57     private int mInitialThreshold;
58 
59     /**
60      * test points:
61      * 1 Get a wake lock at the level of the flags parameter
62      * 2 Force the device to go to sleep
63      * 3 User activity happened
64      */
testPowerManager()65     public void testPowerManager() throws InterruptedException {
66         PowerManager pm = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
67 
68         WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, TAG);
69         wl.acquire(TIME);
70         assertTrue(wl.isHeld());
71         Thread.sleep(TIME + MORE_TIME);
72         assertFalse(wl.isHeld());
73 
74         try {
75             pm.reboot("Testing");
76             fail("reboot should throw SecurityException");
77         } catch (SecurityException e) {
78             // expected
79         }
80     }
81 
82     @Before
setUp()83     public void setUp() {
84         // store the current value so we can restore it
85         ContentResolver resolver = getContext().getContentResolver();
86         mInitialPowerSaverMode = Global.getInt(resolver, Global.AUTOMATIC_POWER_SAVE_MODE, 0);
87         mInitialDynamicPowerSavingsEnabled =
88                 Global.getInt(resolver, Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0);
89         mInitialThreshold =
90                 Global.getInt(resolver, Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 0);
91 
92     }
93 
94     @After
tearDown()95     public void tearDown() {
96         SystemUtil.runWithShellPermissionIdentity(() -> {
97             ContentResolver resolver = getContext().getContentResolver();
98 
99             // Verify we can change it to dynamic.
100             Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVE_MODE, mInitialPowerSaverMode);
101             Global.putInt(resolver,
102                     Global.DYNAMIC_POWER_SAVINGS_ENABLED, mInitialDynamicPowerSavingsEnabled);
103             Global.putInt(resolver,
104                     Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, mInitialThreshold);
105         });
106     }
107 
testPowerManager_getPowerSaveMode()108     public void testPowerManager_getPowerSaveMode() {
109         PowerManager manager = BatteryUtils.getPowerManager();
110         if (batterySaverSupportedCheckApi() && !manager.isBatterySaverSupported()) {
111             return;
112         }
113 
114         SystemUtil.runWithShellPermissionIdentity(() -> {
115             ContentResolver resolver = getContext().getContentResolver();
116 
117             // Verify we can change it to percentage.
118             Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVE_MODE, 0);
119             assertEquals(
120                     PowerManager.POWER_SAVE_MODE_TRIGGER_PERCENTAGE,
121                     manager.getPowerSaveModeTrigger());
122 
123             // Verify we can change it to dynamic.
124             Global.putInt(resolver, Global.AUTOMATIC_POWER_SAVE_MODE, 1);
125             assertEquals(
126                     PowerManager.POWER_SAVE_MODE_TRIGGER_DYNAMIC,
127                     manager.getPowerSaveModeTrigger());
128         });
129     }
130 
testPowerManager_setDynamicPowerSavings()131     public void testPowerManager_setDynamicPowerSavings() {
132         PowerManager manager = BatteryUtils.getPowerManager();
133         if (batterySaverSupportedCheckApi() && !manager.isBatterySaverSupported()) {
134             return;
135         }
136 
137         SystemUtil.runWithShellPermissionIdentity(() -> {
138             ContentResolver resolver = getContext().getContentResolver();
139 
140             // Verify settings are actually updated.
141             manager.setDynamicPowerSaveHint(true, 80);
142             assertEquals(1, Global.getInt(resolver, Global.DYNAMIC_POWER_SAVINGS_ENABLED, 0));
143             assertEquals(80, Global.getInt(resolver,
144                     Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 0));
145 
146             manager.setDynamicPowerSaveHint(false, 20);
147             assertEquals(0, Global.getInt(resolver, Global.DYNAMIC_POWER_SAVINGS_ENABLED, 1));
148             assertEquals(20, Global.getInt(resolver,
149                     Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, 0));
150         });
151     }
152 
153     @LargeTest
testPowerManager_batteryDischargePrediction()154     public void testPowerManager_batteryDischargePrediction() throws Exception {
155         final PowerManager manager = BatteryUtils.getPowerManager();
156         if (batterySaverSupportedCheckApi() && !manager.isBatterySaverSupported()) {
157             return;
158         }
159 
160         if (!BatteryUtils.hasBattery()) {
161             assertNull(manager.getBatteryDischargePrediction());
162             return;
163         }
164 
165         // Unplug to ensure the plugged in broadcast is sent.
166         BatteryUtils.runDumpsysBatteryUnplug();
167 
168         // Plugged in. No prediction should be given.
169         final CallbackAsserter pluggedBroadcastAsserter = CallbackAsserter.forBroadcast(
170                 new IntentFilter(Intent.ACTION_POWER_CONNECTED));
171         BatteryUtils.runDumpsysBatterySetPluggedIn(true);
172         pluggedBroadcastAsserter.assertCalled("Didn't get power connected broadcast",
173                 BROADCAST_TIMEOUT_SECONDS);
174         // PowerManagerService may get the BATTERY_CHANGED broadcast after we get our broadcast,
175         // so we have to have a little wait.
176         waitUntil("PowerManager doesn't think the device has connected power",
177                 () -> manager.getBatteryDischargePrediction() == null);
178 
179         // Not plugged in. At the very least, the basic discharge estimation should be returned.
180         final CallbackAsserter unpluggedBroadcastAsserter = CallbackAsserter.forBroadcast(
181                 new IntentFilter(Intent.ACTION_POWER_DISCONNECTED));
182         BatteryUtils.runDumpsysBatteryUnplug();
183         unpluggedBroadcastAsserter.assertCalled("Didn't get power disconnected broadcast",
184                 BROADCAST_TIMEOUT_SECONDS);
185         // PowerManagerService may get the BATTERY_CHANGED broadcast after we get our broadcast,
186         // so we have to have a little wait.
187         waitUntil("PowerManager still thinks the device has connected power",
188                 () -> manager.getBatteryDischargePrediction() != null);
189 
190         CallbackAsserter predictionChangedBroadcastAsserter = CallbackAsserter.forBroadcast(
191                 new IntentFilter(PowerManager.ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED));
192         setDischargePrediction(LONG_DISCHARGE_DURATION, true);
193         assertDischargePrediction(LONG_DISCHARGE_DURATION, true);
194         predictionChangedBroadcastAsserter.assertCalled("Prediction changed broadcast not received",
195                 BROADCAST_TIMEOUT_SECONDS);
196 
197 
198         predictionChangedBroadcastAsserter = CallbackAsserter.forBroadcast(
199                 new IntentFilter(PowerManager.ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED));
200         setDischargePrediction(SHORT_DISCHARGE_DURATION, false);
201         assertDischargePrediction(SHORT_DISCHARGE_DURATION, false);
202         predictionChangedBroadcastAsserter.assertCalled("Prediction changed broadcast not received",
203                 BROADCAST_TIMEOUT_SECONDS);
204     }
205 
setDischargePrediction(Duration d, boolean isPersonalized)206     private void setDischargePrediction(Duration d, boolean isPersonalized) {
207         final PowerManager manager = BatteryUtils.getPowerManager();
208         SystemUtil.runWithShellPermissionIdentity(
209                 () -> manager.setBatteryDischargePrediction(d, isPersonalized),
210                 android.Manifest.permission.BATTERY_PREDICTION);
211     }
212 
assertDischargePrediction(Duration d, boolean isPersonalized)213     private void assertDischargePrediction(Duration d, boolean isPersonalized) {
214         final PowerManager manager = BatteryUtils.getPowerManager();
215         // We can't pause time so must use >= because the time remaining should decrease as
216         // time goes on.
217         Duration prediction = manager.getBatteryDischargePrediction();
218         assertTrue("Prediction is greater than " + d.toMillis() + "ms: "
219                 + prediction, d.toMillis() >= prediction.toMillis());
220         assertEquals(isPersonalized, manager.isBatteryDischargePredictionPersonalized());
221     }
222 }
223