• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.os.cts.batterysaving;
17 
18 import static android.os.cts.batterysaving.common.Values.APP_25_PACKAGE;
19 import static android.os.cts.batterysaving.common.Values.getRandomInt;
20 
21 import static com.android.compatibility.common.util.AmUtils.runKill;
22 import static com.android.compatibility.common.util.AmUtils.runMakeUidIdle;
23 import static com.android.compatibility.common.util.BatteryUtils.enableBatterySaver;
24 import static com.android.compatibility.common.util.BatteryUtils.runDumpsysBatteryUnplug;
25 import static com.android.compatibility.common.util.SettingsUtils.putGlobalSetting;
26 import static com.android.compatibility.common.util.TestUtils.waitUntil;
27 
28 import static org.junit.Assert.assertEquals;
29 import static org.junit.Assert.assertTrue;
30 
31 import android.app.AlarmManager;
32 import android.content.BroadcastReceiver;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.content.IntentFilter;
36 import android.os.Handler;
37 import android.os.Looper;
38 import android.os.SystemClock;
39 import android.os.cts.batterysaving.common.BatterySavingCtsCommon.Payload;
40 import android.os.cts.batterysaving.common.BatterySavingCtsCommon.Payload.TestServiceRequest;
41 import android.os.cts.batterysaving.common.BatterySavingCtsCommon.Payload.TestServiceRequest.SetAlarmRequest;
42 import android.os.cts.batterysaving.common.BatterySavingCtsCommon.Payload.TestServiceRequest.StartServiceRequest;
43 import android.os.cts.batterysaving.common.Values;
44 import android.util.Log;
45 
46 import androidx.test.filters.LargeTest;
47 import androidx.test.filters.MediumTest;
48 import androidx.test.runner.AndroidJUnit4;
49 
50 import com.android.compatibility.common.util.ThreadUtils;
51 
52 import org.junit.After;
53 import org.junit.Before;
54 import org.junit.Test;
55 import org.junit.runner.RunWith;
56 
57 import java.io.IOException;
58 import java.util.concurrent.atomic.AtomicInteger;
59 
60 /**
61  * CTS for battery saver alarm throttling
62  *
63  atest $ANDROID_BUILD_TOP/cts/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySaverAlarmTest.java
64  */
65 @MediumTest
66 @RunWith(AndroidJUnit4.class)
67 public class BatterySaverAlarmTest extends BatterySavingTestBase {
68     private static final String TAG = "BatterySaverAlarmTest";
69 
70     private static final long DEFAULT_WAIT = 1_000;
71 
72     // Tweaked alarm manager constants to facilitate testing
73     private static final long MIN_REPEATING_INTERVAL = 5_000;
74     private static final long ALLOW_WHILE_IDLE_SHORT_TIME = 10_000;
75     private static final long ALLOW_WHILE_IDLE_LONG_TIME = 20_000;
76     private static final long MIN_FUTURITY = 2_000;
77 
updateAlarmManagerConstants()78     private void updateAlarmManagerConstants() throws IOException {
79         putGlobalSetting("alarm_manager_constants",
80                 "min_interval=" + MIN_REPEATING_INTERVAL + ","
81                 + "min_futurity=" + MIN_FUTURITY + ","
82                 + "allow_while_idle_short_time=" + ALLOW_WHILE_IDLE_SHORT_TIME + ","
83                 + "allow_while_idle_long_time=" + ALLOW_WHILE_IDLE_LONG_TIME);
84     }
85 
resetAlarmManagerConstants()86     private void resetAlarmManagerConstants() throws IOException {
87         putGlobalSetting("alarm_manager_constants", "null");
88     }
89 
90     // Use a different broadcast action every time.
91     private final String ACTION = "BATTERY_SAVER_ALARM_TEST_ALARM_ACTION_" + Values.getRandomInt();
92 
93     private final AtomicInteger mAlarmCount = new AtomicInteger();
94 
95     private final BroadcastReceiver mAlarmReceiver = new BroadcastReceiver() {
96         @Override
97         public void onReceive(Context context, Intent intent) {
98             mAlarmCount.incrementAndGet();
99             Log.d(TAG, "Alarm received at " + SystemClock.elapsedRealtime());
100         }
101     };
102 
103     @Before
setUp()104     public void setUp() throws IOException {
105         updateAlarmManagerConstants();
106 
107         final IntentFilter filter = new IntentFilter(ACTION);
108         getContext().registerReceiver(mAlarmReceiver, filter, null,
109                 new Handler(Looper.getMainLooper()));
110     }
111 
112     @After
tearDown()113     public void tearDown() throws IOException {
114         resetAlarmManagerConstants();
115         getContext().unregisterReceiver(mAlarmReceiver);
116     }
117 
scheduleAlarm(String targetPackage, int type, long triggerMillis)118     private void scheduleAlarm(String targetPackage, int type, long triggerMillis)
119             throws Exception {
120         scheduleAlarm(targetPackage, type, triggerMillis, /*whileIdle=*/ true);
121     }
122 
scheduleAlarm(String targetPackage, int type, long triggerMillis, boolean whileIdle)123     private void scheduleAlarm(String targetPackage, int type, long triggerMillis,
124             boolean whileIdle) throws Exception {
125         Log.d(TAG, "Setting an alarm at " + triggerMillis + " (in "
126                 + (triggerMillis - SystemClock.elapsedRealtime()) + "ms)");
127         final SetAlarmRequest areq = SetAlarmRequest.newBuilder()
128                 .setIntentAction(ACTION)
129                 .setType(type)
130                 .setAllowWhileIdle(whileIdle)
131                 .setTriggerTime(triggerMillis)
132                 .build();
133         final Payload response = mRpc.sendRequest(targetPackage,
134                 Payload.newBuilder().setTestServiceRequest(
135                         TestServiceRequest.newBuilder().setSetAlarm(areq))
136                         .build());
137         assertTrue(response.hasTestServiceResponse()
138                 && response.getTestServiceResponse().getSetAlarmAck());
139     }
140 
141     /**
142      * Return a service in the target package.
143      */
startService(String targetPackage, boolean foreground)144     private String startService(String targetPackage, boolean foreground)
145             throws Exception {
146         final String action = "start_service_" + getRandomInt() + "_fg=" + foreground;
147 
148         final Payload response = mRpc.sendRequest(targetPackage,
149                 Payload.newBuilder().setTestServiceRequest(
150                         TestServiceRequest.newBuilder().setStartService(
151                                 StartServiceRequest.newBuilder()
152                                         .setForeground(foreground)
153                                         .setAction(action).build()
154                         )).build());
155         assertTrue(response.hasTestServiceResponse()
156                 && response.getTestServiceResponse().getStartServiceAck());
157         return action;
158     }
159 
stopService(String targetPackage)160     private void stopService(String targetPackage) throws Exception {
161         final Payload response = mRpc.sendRequest(targetPackage,
162                 Payload.newBuilder().setTestServiceRequest(
163                         TestServiceRequest.newBuilder().setStopService(true).build()).build());
164         assertTrue(response.hasTestServiceResponse()
165                 && response.getTestServiceResponse().getStopServiceAck());
166     }
167 
168 
forcePackageIntoBg(String packageName)169     private void forcePackageIntoBg(String packageName) throws Exception {
170         stopService(packageName);
171         runMakeUidIdle(packageName);
172         Thread.sleep(200);
173         runKill(packageName, /*wait=*/ true);
174         Thread.sleep(1000);
175     }
176 
177     @LargeTest
178     @Test
testAllowWhileIdleThrottled()179     public void testAllowWhileIdleThrottled() throws Exception {
180         final String targetPackage = APP_25_PACKAGE;
181 
182         runDumpsysBatteryUnplug();
183 
184         enableBatterySaver(true);
185 
186         forcePackageIntoBg(targetPackage);
187 
188         // First alarm shouldn't be throttled.
189         final long triggerElapsed1 = SystemClock.elapsedRealtime() + MIN_FUTURITY;
190         scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed1);
191         ThreadUtils.sleepUntilRealtime(triggerElapsed1 + DEFAULT_WAIT);
192         assertEquals("Allow-while-idle alarm shouldn't be blocked in battery saver",
193                 1, mAlarmCount.get());
194 
195         // Second one should be throttled.
196         mAlarmCount.set(0);
197 
198         final long triggerElapsed2 = triggerElapsed1 + ALLOW_WHILE_IDLE_SHORT_TIME;
199         scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed2);
200         ThreadUtils.sleepUntilRealtime(triggerElapsed2 + DEFAULT_WAIT);
201         assertEquals("Follow up allow-while-idle alarm shouldn't go off before short time",
202                 0, mAlarmCount.get());
203 
204         final long triggerElapsed3 = triggerElapsed1 + ALLOW_WHILE_IDLE_LONG_TIME;
205         ThreadUtils.sleepUntilRealtime(triggerElapsed3 + DEFAULT_WAIT);
206         assertEquals("Follow-up allow-while-idle alarm should go off after long time",
207                 1, mAlarmCount.get());
208 
209         // Start an FG service, which should reset throttling.
210         mAlarmCount.set(0);
211 
212         startService(targetPackage, true);
213 
214         final long triggerElapsed4 = triggerElapsed3 + ALLOW_WHILE_IDLE_SHORT_TIME;
215         scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed4);
216         ThreadUtils.sleepUntilRealtime(triggerElapsed4 + DEFAULT_WAIT);
217         assertEquals("Allow-while-idle alarm shouldn't be throttled in battery saver"
218                 +" after FG service started",
219                 1, mAlarmCount.get());
220 
221         stopService(targetPackage);
222         // Battery saver off. Always use the short time.
223         enableBatterySaver(false);
224 
225         mAlarmCount.set(0);
226 
227         final long triggerElapsed5 = triggerElapsed4 + ALLOW_WHILE_IDLE_SHORT_TIME;
228         scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed5);
229         ThreadUtils.sleepUntilRealtime(triggerElapsed5 + DEFAULT_WAIT);
230         assertEquals("Allow-while-idle alarm shouldn't be throttled in battery saver"
231                         +" when BS is off",
232                 1, mAlarmCount.get());
233 
234         // One more time.
235         mAlarmCount.set(0);
236 
237         final long triggerElapsed6 = triggerElapsed5 + ALLOW_WHILE_IDLE_SHORT_TIME;
238         scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed6);
239         ThreadUtils.sleepUntilRealtime(triggerElapsed6 + DEFAULT_WAIT);
240         assertEquals("Allow-while-idle alarm shouldn't be throttled when BS is off",
241                 1, mAlarmCount.get());
242     }
243 
244     @LargeTest
245     @Test
testAlarmsThrottled()246     public void testAlarmsThrottled() throws Exception {
247         final String targetPackage = APP_25_PACKAGE;
248 
249         runDumpsysBatteryUnplug();
250 
251         enableBatterySaver(true);
252 
253         forcePackageIntoBg(targetPackage);
254 
255         {
256             // When battery saver is enabled, alarms should be blocked.
257             final long triggerElapsed = SystemClock.elapsedRealtime() + MIN_FUTURITY;
258             scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed,
259                     /* whileIdle=*/ false);
260             ThreadUtils.sleepUntilRealtime(triggerElapsed + DEFAULT_WAIT);
261             assertEquals("Non-while-idle alarm should be blocked in battery saver",
262                     0, mAlarmCount.get());
263         }
264 
265         // Start an FG service -> should unblock the alarm.
266         startService(targetPackage, true);
267 
268         waitUntil("Alarm should fire for an FG app",
269                 () -> mAlarmCount.get() == 1);
270 
271         stopService(targetPackage);
272         // Try again.
273         mAlarmCount.set(0);
274 
275         forcePackageIntoBg(targetPackage);
276 
277         // Try again.
278         // When battery saver is enabled, alarms should be blocked.
279         {
280             final long triggerElapsed = SystemClock.elapsedRealtime() + MIN_FUTURITY;
281             scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed,
282                     /* whileIdle=*/ false);
283             ThreadUtils.sleepUntilRealtime(triggerElapsed + DEFAULT_WAIT);
284             assertEquals("Non-while-idle alarm should be blocked in battery saver",
285                     0, mAlarmCount.get());
286         }
287 
288         // This time, disable EBS -> should unblock the alarm.
289         enableBatterySaver(false);
290         waitUntil("Allow-while-idle alarm should be blocked in battery saver",
291                 () -> mAlarmCount.get() == 1);
292     }
293 }
294