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