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 private static final long THROTTLED_WAIT = 5_000; 72 73 // Tweaked alarm manager constants to facilitate testing 74 private static final long MIN_REPEATING_INTERVAL = 5_000; 75 private static final long ALLOW_WHILE_IDLE_SHORT_TIME = 10_000; 76 private static final long ALLOW_WHILE_IDLE_LONG_TIME = 20_000; 77 private static final long MIN_FUTURITY = 2_000; 78 updateAlarmManagerConstants()79 private void updateAlarmManagerConstants() throws IOException { 80 putGlobalSetting("alarm_manager_constants", 81 "min_interval=" + MIN_REPEATING_INTERVAL + "," 82 + "min_futurity=" + MIN_FUTURITY + "," 83 + "allow_while_idle_short_time=" + ALLOW_WHILE_IDLE_SHORT_TIME + "," 84 + "allow_while_idle_long_time=" + ALLOW_WHILE_IDLE_LONG_TIME); 85 } 86 resetAlarmManagerConstants()87 private void resetAlarmManagerConstants() throws IOException { 88 putGlobalSetting("alarm_manager_constants", "null"); 89 } 90 91 // Use a different broadcast action every time. 92 private final String ACTION = "BATTERY_SAVER_ALARM_TEST_ALARM_ACTION_" + Values.getRandomInt(); 93 94 private final AtomicInteger mAlarmCount = new AtomicInteger(); 95 96 private final BroadcastReceiver mAlarmReceiver = new BroadcastReceiver() { 97 @Override 98 public void onReceive(Context context, Intent intent) { 99 mAlarmCount.incrementAndGet(); 100 Log.d(TAG, "Alarm received at " + SystemClock.elapsedRealtime()); 101 } 102 }; 103 104 @Before setUp()105 public void setUp() throws IOException { 106 updateAlarmManagerConstants(); 107 108 final IntentFilter filter = new IntentFilter(ACTION); 109 getContext().registerReceiver(mAlarmReceiver, filter, null, 110 new Handler(Looper.getMainLooper())); 111 } 112 113 @After tearDown()114 public void tearDown() throws IOException { 115 resetAlarmManagerConstants(); 116 getContext().unregisterReceiver(mAlarmReceiver); 117 } 118 scheduleAlarm(String targetPackage, int type, long triggerMillis)119 private void scheduleAlarm(String targetPackage, int type, long triggerMillis) 120 throws Exception { 121 scheduleAlarm(targetPackage, type, triggerMillis, /*whileIdle=*/ true); 122 } 123 scheduleAlarm(String targetPackage, int type, long triggerMillis, boolean whileIdle)124 private void scheduleAlarm(String targetPackage, int type, long triggerMillis, 125 boolean whileIdle) throws Exception { 126 Log.d(TAG, "Setting an alarm at " + triggerMillis + " (in " 127 + (triggerMillis - SystemClock.elapsedRealtime()) + "ms)"); 128 final SetAlarmRequest areq = SetAlarmRequest.newBuilder() 129 .setIntentAction(ACTION) 130 .setType(type) 131 .setAllowWhileIdle(whileIdle) 132 .setTriggerTime(triggerMillis) 133 .build(); 134 final Payload response = mRpc.sendRequest(targetPackage, 135 Payload.newBuilder().setTestServiceRequest( 136 TestServiceRequest.newBuilder().setSetAlarm(areq)) 137 .build()); 138 assertTrue(response.hasTestServiceResponse() 139 && response.getTestServiceResponse().getSetAlarmAck()); 140 } 141 142 /** 143 * Return a service in the target package. 144 */ startService(String targetPackage, boolean foreground)145 private String startService(String targetPackage, boolean foreground) 146 throws Exception { 147 final String action = "start_service_" + getRandomInt() + "_fg=" + foreground; 148 149 final Payload response = mRpc.sendRequest(targetPackage, 150 Payload.newBuilder().setTestServiceRequest( 151 TestServiceRequest.newBuilder().setStartService( 152 StartServiceRequest.newBuilder() 153 .setForeground(foreground) 154 .setAction(action).build() 155 )).build()); 156 assertTrue(response.hasTestServiceResponse() 157 && response.getTestServiceResponse().getStartServiceAck()); 158 return action; 159 } 160 stopService(String targetPackage)161 private void stopService(String targetPackage) throws Exception { 162 final Payload response = mRpc.sendRequest(targetPackage, 163 Payload.newBuilder().setTestServiceRequest( 164 TestServiceRequest.newBuilder().setStopService(true).build()).build()); 165 assertTrue(response.hasTestServiceResponse() 166 && response.getTestServiceResponse().getStopServiceAck()); 167 } 168 169 forcePackageIntoBg(String packageName)170 private void forcePackageIntoBg(String packageName) throws Exception { 171 stopService(packageName); 172 runMakeUidIdle(packageName); 173 Thread.sleep(200); 174 runKill(packageName, /*wait=*/ true); 175 Thread.sleep(1000); 176 } 177 178 @LargeTest 179 @Test testAllowWhileIdleThrottled()180 public void testAllowWhileIdleThrottled() throws Exception { 181 final String targetPackage = APP_25_PACKAGE; 182 183 runDumpsysBatteryUnplug(); 184 185 enableBatterySaver(true); 186 187 forcePackageIntoBg(targetPackage); 188 189 // First alarm shouldn't be throttled. 190 long now = SystemClock.elapsedRealtime(); 191 final long triggerElapsed1 = now + MIN_FUTURITY; 192 scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed1); 193 ThreadUtils.sleepUntilRealtime(triggerElapsed1 + DEFAULT_WAIT); 194 assertEquals("Allow-while-idle alarm shouldn't be blocked in battery saver", 195 1, mAlarmCount.get()); 196 197 // Second one should be throttled. 198 mAlarmCount.set(0); 199 200 // Check that the alarm scheduled at triggerElapsed2 201 // fires between triggerElapsed2 and (triggerElapsed3+THROTTLED_WAIT). 202 now = SystemClock.elapsedRealtime(); 203 final long triggerElapsed2 = now + ALLOW_WHILE_IDLE_SHORT_TIME; 204 final long triggerElapsed3 = now + ALLOW_WHILE_IDLE_LONG_TIME; 205 scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed2); 206 207 // Check the time first before checking the alarm counter to avoid a 208 // situation when the alarm fires between sleepUntilRealtime and 209 // assertEquals. 210 while (true) { 211 Thread.sleep(DEFAULT_WAIT); 212 213 final int alarmCount = mAlarmCount.get(); 214 if (SystemClock.elapsedRealtime() < triggerElapsed2) { 215 assertEquals("Follow up allow-while-idle alarm shouldn't go off " 216 + "before short time", 217 0, alarmCount); 218 } else { 219 break; 220 } 221 } 222 223 ThreadUtils.sleepUntilRealtime(triggerElapsed3 + THROTTLED_WAIT); 224 assertEquals("Follow-up allow-while-idle alarm should go off after long time", 225 1, mAlarmCount.get()); 226 227 // Start an FG service, which should reset throttling. 228 mAlarmCount.set(0); 229 230 startService(targetPackage, true); 231 232 now = SystemClock.elapsedRealtime(); 233 final long triggerElapsed4 = now + ALLOW_WHILE_IDLE_SHORT_TIME; 234 scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed4); 235 ThreadUtils.sleepUntilRealtime(triggerElapsed4 + DEFAULT_WAIT); 236 assertEquals("Allow-while-idle alarm shouldn't be throttled in battery saver" 237 +" after FG service started", 238 1, mAlarmCount.get()); 239 240 stopService(targetPackage); 241 // Battery saver off. Always use the short time. 242 enableBatterySaver(false); 243 244 mAlarmCount.set(0); 245 246 now = SystemClock.elapsedRealtime(); 247 final long triggerElapsed5 = now + ALLOW_WHILE_IDLE_SHORT_TIME; 248 scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed5); 249 ThreadUtils.sleepUntilRealtime(triggerElapsed5 + DEFAULT_WAIT); 250 assertEquals("Allow-while-idle alarm shouldn't be throttled in battery saver" 251 +" when BS is off", 252 1, mAlarmCount.get()); 253 254 // One more time. 255 mAlarmCount.set(0); 256 257 now = SystemClock.elapsedRealtime(); 258 final long triggerElapsed6 = now + ALLOW_WHILE_IDLE_SHORT_TIME; 259 scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed6); 260 ThreadUtils.sleepUntilRealtime(triggerElapsed6 + DEFAULT_WAIT); 261 assertEquals("Allow-while-idle alarm shouldn't be throttled when BS is off", 262 1, mAlarmCount.get()); 263 } 264 265 @LargeTest 266 @Test testAlarmsThrottled()267 public void testAlarmsThrottled() throws Exception { 268 final String targetPackage = APP_25_PACKAGE; 269 270 runDumpsysBatteryUnplug(); 271 272 enableBatterySaver(true); 273 274 forcePackageIntoBg(targetPackage); 275 276 { 277 // When battery saver is enabled, alarms should be blocked. 278 final long triggerElapsed = SystemClock.elapsedRealtime() + MIN_FUTURITY; 279 scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed, 280 /* whileIdle=*/ false); 281 ThreadUtils.sleepUntilRealtime(triggerElapsed + DEFAULT_WAIT); 282 assertEquals("Non-while-idle alarm should be blocked in battery saver", 283 0, mAlarmCount.get()); 284 } 285 286 // Start an FG service -> should unblock the alarm. 287 startService(targetPackage, true); 288 289 waitUntil("Alarm should fire for an FG app", 290 () -> mAlarmCount.get() == 1); 291 292 stopService(targetPackage); 293 // Try again. 294 mAlarmCount.set(0); 295 296 forcePackageIntoBg(targetPackage); 297 298 // Try again. 299 // When battery saver is enabled, alarms should be blocked. 300 { 301 final long triggerElapsed = SystemClock.elapsedRealtime() + MIN_FUTURITY; 302 scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed, 303 /* whileIdle=*/ false); 304 ThreadUtils.sleepUntilRealtime(triggerElapsed + DEFAULT_WAIT); 305 assertEquals("Non-while-idle alarm should be blocked in battery saver", 306 0, mAlarmCount.get()); 307 } 308 309 // This time, disable EBS -> should unblock the alarm. 310 enableBatterySaver(false); 311 waitUntil("Allow-while-idle alarm should be blocked in battery saver", 312 () -> mAlarmCount.get() == 1); 313 } 314 } 315