1 /*
2  * Copyright (C) 2018 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 package com.android.compatibility.common.util;
17 
18 import static android.os.Flags.batterySaverSupportedCheckApi;
19 
20 import static com.android.compatibility.common.util.TestUtils.waitUntil;
21 
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.pm.PackageManager;
25 import android.os.BatteryManager;
26 import android.os.PowerManager;
27 import android.provider.Settings.Global;
28 import android.util.Log;
29 
30 import androidx.test.InstrumentationRegistry;
31 
32 import com.android.compatibility.common.util.UserSettings.Namespace;
33 
34 import org.junit.Assume;
35 
36 public class BatteryUtils {
37     private static final String TAG = "CtsBatteryUtils";
38 
BatteryUtils()39     private BatteryUtils() {
40     }
41 
getBatteryManager()42     public static BatteryManager getBatteryManager() {
43         return InstrumentationRegistry.getContext().getSystemService(BatteryManager.class);
44     }
45 
getPowerManager()46     public static PowerManager getPowerManager() {
47         return InstrumentationRegistry.getContext().getSystemService(PowerManager.class);
48     }
49 
hasBattery()50     public static boolean hasBattery() {
51         final Intent batteryInfo = InstrumentationRegistry.getContext()
52                 .registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
53         return batteryInfo != null
54             && batteryInfo.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
55     }
56 
57     /** Make the target device think it's off charger. */
runDumpsysBatteryUnplug()58     public static void runDumpsysBatteryUnplug() throws Exception {
59         SystemUtil.runShellCommandForNoOutput("cmd battery unplug");
60 
61         waitForPlugStatus(false);
62 
63         Log.d(TAG, "Battery UNPLUGGED");
64     }
65 
66     /**
67      * Set the battery level to {@code level} percent. The valid range is [0, 100].
68      */
runDumpsysBatterySetLevel(int level)69     public static void runDumpsysBatterySetLevel(int level) throws Exception {
70         SystemUtil.runShellCommandForNoOutput(("cmd battery set level " + level));
71 
72         Log.d(TAG, "Battery level set to " + level);
73     }
74 
75     /**
76      * Set whether the device is plugged in to a charger or not.
77      */
runDumpsysBatterySetPluggedIn(boolean pluggedIn)78     public static void runDumpsysBatterySetPluggedIn(boolean pluggedIn) throws Exception {
79         SystemUtil.runShellCommandForNoOutput(("cmd battery set ac " + (pluggedIn ? "1" : "0")));
80 
81         waitForPlugStatus(pluggedIn);
82 
83         Log.d(TAG, "Battery AC set to " + pluggedIn);
84     }
85 
waitForPlugStatus(boolean pluggedIn)86     private static void waitForPlugStatus(boolean pluggedIn) throws Exception {
87         if (InstrumentationRegistry.getContext().getPackageManager().isInstantApp()) {
88             // Instant apps are not allowed to query ACTION_BATTERY_CHANGED. Add short sleep as
89             // best-effort wait for status.
90             Thread.sleep(2000);
91             return;
92         }
93         IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
94         waitUntil("Device still " + (pluggedIn ? " not plugged" : " plugged"),
95                 () -> {
96                     Intent batteryStatus =
97                             InstrumentationRegistry.getContext().registerReceiver(null, ifilter);
98                     int chargePlug = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
99                     return pluggedIn == (chargePlug != 0);
100                 });
101     }
102 
103     /** Reset the effect of all the previous {@code runDumpsysBattery*} call */
runDumpsysBatteryReset()104     public static void runDumpsysBatteryReset() {
105         SystemUtil.runShellCommandForNoOutput(("cmd battery reset"));
106 
107         Log.d(TAG, "Battery RESET");
108     }
109 
enableAdaptiveBatterySaver(boolean enabled)110     public static void enableAdaptiveBatterySaver(boolean enabled) {
111         final String setting = enabled ? "true" : "false";
112         SystemUtil.runShellCommandForNoOutput(
113                 "cmd power set-adaptive-power-saver-enabled " + setting);
114     }
115 
116     /**
117      * Enable / disable battery saver. Note {@link #runDumpsysBatteryUnplug} must have been executed
118      * before enabling BS. Always wait until battery saver is turned on or off according to the
119      *  input. So this method can only be used for battery saver is supported.
120      */
enableBatterySaver(boolean enabled)121     public static void enableBatterySaver(boolean enabled) throws Exception {
122         enableBatterySaver(enabled, /* shouldWait= */ true);
123     }
124 
125     /**
126      * Enable / disable battery saver. Note {@link #runDumpsysBatteryUnplug} must have been
127      * executed before enabling BS. Only wait until battery saver when required.
128      */
enableBatterySaver(boolean enabled, boolean shouldWait)129     public static void enableBatterySaver(boolean enabled, boolean shouldWait) throws Exception {
130         UserSettings globalSettings = new UserSettings(Namespace.GLOBAL);
131         if (enabled) {
132             SystemUtil.runShellCommandForNoOutput("cmd power set-mode 1");
133             AmUtils.waitForBroadcastBarrier();
134             globalSettings.set(Global.LOW_POWER_MODE, "1");
135             if (shouldWait) {
136                 waitUntil("Battery saver still off", () -> getPowerManager().isPowerSaveMode());
137             }
138         } else {
139             SystemUtil.runShellCommandForNoOutput("cmd power set-mode 0");
140             AmUtils.waitForBroadcastBarrier();
141             globalSettings.set(Global.LOW_POWER_MODE, "0");
142             globalSettings.set(Global.LOW_POWER_MODE_STICKY, "0");
143             if (shouldWait) {
144                 waitUntil("Battery saver still on", () -> !getPowerManager().isPowerSaveMode());
145             }
146         }
147 
148         AmUtils.waitForBroadcastBarrier();
149         Log.d(TAG, "Battery saver turned " + (enabled ? "ON" : "OFF"));
150     }
151 
152     /** Reset battery saver state and take it out of a forced state. */
resetBatterySaver()153     public static void resetBatterySaver() throws Exception {
154         UserSettings globalSettings = new UserSettings(Namespace.GLOBAL);
155         SystemUtil.runShellCommandForNoOutput("cmd power set-mode 0");
156         globalSettings.set(Global.LOW_POWER_MODE, null);
157         globalSettings.set(Global.LOW_POWER_MODE_STICKY, null);
158         waitUntil("Battery saver still on", () -> !getPowerManager().isPowerSaveMode());
159     }
160 
161     /**
162      * Turn on/off screen.
163      */
turnOnScreen(boolean on)164     public static void turnOnScreen(boolean on) throws Exception {
165         if (on) {
166             SystemUtil.runShellCommandForNoOutput("input keyevent KEYCODE_WAKEUP");
167             waitUntil("Device still not interactive", () -> getPowerManager().isInteractive());
168 
169         } else {
170             SystemUtil.runShellCommandForNoOutput("input keyevent KEYCODE_SLEEP");
171             waitUntil("Device still interactive", () -> !getPowerManager().isInteractive());
172         }
173         AmUtils.waitForBroadcastBarrier();
174         Log.d(TAG, "Screen turned " + (on ? "ON" : "OFF"));
175     }
176 
177     /** @return true if the device supports battery saver. */
isBatterySaverSupported()178     public static boolean isBatterySaverSupported() {
179         final PowerManager powerManager = getPowerManager();
180         if (batterySaverSupportedCheckApi() && !powerManager.isBatterySaverSupported()) {
181             // Battery saver configuration has been turned off for devices.
182             return false;
183         }
184 
185         if (!hasBattery()) {
186             // Devices without a battery don't support battery saver.
187             return false;
188         }
189 
190         final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
191         return !(pm.hasSystemFeature(PackageManager.FEATURE_WATCH) ||
192             pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE));
193     }
194 
195     /** "Assume" the current device supports battery saver. */
assumeBatterySaverFeature()196     public static void assumeBatterySaverFeature() {
197         Assume.assumeTrue("Device doesn't support battery saver", isBatterySaverSupported());
198     }
199 }
200