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