blob: 4fc9dd68177254d2d633b05813c7af022b4664dc [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os.cts.batterysaving;
import static android.os.cts.batterysaving.common.Values.APP_25_PACKAGE;
import static android.os.cts.batterysaving.common.Values.getRandomInt;
import static com.android.compatibility.common.util.AmUtils.runKill;
import static com.android.compatibility.common.util.AmUtils.runMakeUidIdle;
import static com.android.compatibility.common.util.BatteryUtils.enableBatterySaver;
import static com.android.compatibility.common.util.BatteryUtils.runDumpsysBatteryUnplug;
import static com.android.compatibility.common.util.SettingsUtils.putGlobalSetting;
import static com.android.compatibility.common.util.TestUtils.waitUntil;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.app.AlarmManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.os.cts.batterysaving.common.BatterySavingCtsCommon.Payload;
import android.os.cts.batterysaving.common.BatterySavingCtsCommon.Payload.TestServiceRequest;
import android.os.cts.batterysaving.common.BatterySavingCtsCommon.Payload.TestServiceRequest.SetAlarmRequest;
import android.os.cts.batterysaving.common.BatterySavingCtsCommon.Payload.TestServiceRequest.StartServiceRequest;
import android.os.cts.batterysaving.common.Values;
import android.util.Log;
import androidx.test.filters.LargeTest;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.ThreadUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
/**
* CTS for battery saver alarm throttling
*
atest $ANDROID_BUILD_TOP/cts/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySaverAlarmTest.java
*/
@MediumTest
@RunWith(AndroidJUnit4.class)
public class BatterySaverAlarmTest extends BatterySavingTestBase {
private static final String TAG = "BatterySaverAlarmTest";
private static final long DEFAULT_WAIT = 1_000;
private static final long POLL_INTERVAL = 200;
// Tweaked alarm manager constants to facilitate testing
private static final long MIN_REPEATING_INTERVAL = 5_000;
private static final long ALLOW_WHILE_IDLE_SHORT_TIME = 10_000;
private static final long ALLOW_WHILE_IDLE_LONG_TIME = 20_000;
private static final long MIN_FUTURITY = 2_000;
private void updateAlarmManagerConstants() throws IOException {
putGlobalSetting("alarm_manager_constants",
"min_interval=" + MIN_REPEATING_INTERVAL + ","
+ "min_futurity=" + MIN_FUTURITY + ","
+ "allow_while_idle_short_time=" + ALLOW_WHILE_IDLE_SHORT_TIME + ","
+ "allow_while_idle_long_time=" + ALLOW_WHILE_IDLE_LONG_TIME);
}
private void resetAlarmManagerConstants() throws IOException {
putGlobalSetting("alarm_manager_constants", "null");
}
// Use a different broadcast action every time.
private final String ACTION = "BATTERY_SAVER_ALARM_TEST_ALARM_ACTION_" + Values.getRandomInt();
private final AtomicInteger mAlarmCount = new AtomicInteger();
private final BroadcastReceiver mAlarmReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mAlarmCount.incrementAndGet();
Log.d(TAG, "Alarm received at " + SystemClock.elapsedRealtime());
}
};
@Before
public void setUp() throws IOException {
updateAlarmManagerConstants();
final IntentFilter filter = new IntentFilter(ACTION);
getContext().registerReceiver(mAlarmReceiver, filter, null,
new Handler(Looper.getMainLooper()));
}
@After
public void tearDown() throws IOException {
resetAlarmManagerConstants();
getContext().unregisterReceiver(mAlarmReceiver);
}
private void scheduleAlarm(String targetPackage, int type, long triggerMillis)
throws Exception {
scheduleAlarm(targetPackage, type, triggerMillis, /*whileIdle=*/ true);
}
private void scheduleAlarm(String targetPackage, int type, long triggerMillis,
boolean whileIdle) throws Exception {
Log.d(TAG, "Setting an alarm at " + triggerMillis + " (in "
+ (triggerMillis - SystemClock.elapsedRealtime()) + "ms)");
final SetAlarmRequest areq = SetAlarmRequest.newBuilder()
.setIntentAction(ACTION)
.setType(type)
.setAllowWhileIdle(whileIdle)
.setTriggerTime(triggerMillis)
.build();
final Payload response = mRpc.sendRequest(targetPackage,
Payload.newBuilder().setTestServiceRequest(
TestServiceRequest.newBuilder().setSetAlarm(areq))
.build());
assertTrue(response.hasTestServiceResponse()
&& response.getTestServiceResponse().getSetAlarmAck());
}
/**
* Return a service in the target package.
*/
private String startService(String targetPackage, boolean foreground)
throws Exception {
final String action = "start_service_" + getRandomInt() + "_fg=" + foreground;
final Payload response = mRpc.sendRequest(targetPackage,
Payload.newBuilder().setTestServiceRequest(
TestServiceRequest.newBuilder().setStartService(
StartServiceRequest.newBuilder()
.setForeground(foreground)
.setAction(action).build()
)).build());
assertTrue(response.hasTestServiceResponse()
&& response.getTestServiceResponse().getStartServiceAck());
return action;
}
private static void forcePackageIntoBg(String packageName) throws Exception {
runMakeUidIdle(packageName);
Thread.sleep(200);
runKill(packageName, /*wait=*/ true);
Thread.sleep(1000);
}
@LargeTest
@Test
public void testAllowWhileIdleThrottled() throws Exception {
final String targetPackage = APP_25_PACKAGE;
runDumpsysBatteryUnplug();
enableBatterySaver(true);
forcePackageIntoBg(targetPackage);
// First alarm shouldn't be throttled.
final long triggerElapsed1 = SystemClock.elapsedRealtime() + MIN_FUTURITY;
scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed1);
ThreadUtils.sleepUntilRealtime(triggerElapsed1 + DEFAULT_WAIT);
assertEquals("Allow-while-idle alarm shouldn't be blocked in battery saver",
1, mAlarmCount.get());
// Second one should be throttled.
mAlarmCount.set(0);
final long triggerElapsed2 = triggerElapsed1 + ALLOW_WHILE_IDLE_SHORT_TIME;
scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed2);
ThreadUtils.sleepUntilRealtime(triggerElapsed2 + DEFAULT_WAIT);
assertEquals("Follow up allow-while-idle alarm shouldn't go off before short time",
0, mAlarmCount.get());
final long triggerElapsed3 = triggerElapsed1 + ALLOW_WHILE_IDLE_LONG_TIME;
ThreadUtils.sleepUntilRealtime(triggerElapsed3 + DEFAULT_WAIT);
assertEquals("Follow-up allow-while-idle alarm should go off after long time",
1, mAlarmCount.get());
// Start an FG service, which should reset throttling.
mAlarmCount.set(0);
startService(targetPackage, true);
final long triggerElapsed4 = triggerElapsed3 + ALLOW_WHILE_IDLE_SHORT_TIME;
scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed4);
ThreadUtils.sleepUntilRealtime(triggerElapsed4 + DEFAULT_WAIT);
assertEquals("Allow-while-idle alarm shouldn't be throttled in battery saver"
+" after FG service started",
1, mAlarmCount.get());
// Battery saver off. Always use the short time.
enableBatterySaver(false);
mAlarmCount.set(0);
final long triggerElapsed5 = triggerElapsed4 + ALLOW_WHILE_IDLE_SHORT_TIME;
scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed5);
ThreadUtils.sleepUntilRealtime(triggerElapsed5 + DEFAULT_WAIT);
assertEquals("Allow-while-idle alarm shouldn't be throttled in battery saver"
+" when BS is off",
1, mAlarmCount.get());
// One more time.
mAlarmCount.set(0);
final long triggerElapsed6 = triggerElapsed5 + ALLOW_WHILE_IDLE_SHORT_TIME;
scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed6);
ThreadUtils.sleepUntilRealtime(triggerElapsed6 + DEFAULT_WAIT);
assertEquals("Allow-while-idle alarm shouldn't be throttled when BS is off",
1, mAlarmCount.get());
}
@LargeTest
@Test
public void testAlarmsThrottled() throws Exception {
final String targetPackage = APP_25_PACKAGE;
runDumpsysBatteryUnplug();
enableBatterySaver(true);
forcePackageIntoBg(targetPackage);
{
// When battery saver is enabled, alarms should be blocked.
final long triggerElapsed = SystemClock.elapsedRealtime() + MIN_FUTURITY;
scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed,
/* whileIdle=*/ false);
ThreadUtils.sleepUntilRealtime(triggerElapsed + DEFAULT_WAIT);
assertEquals("Non-while-idle alarm should be blocked in battery saver",
0, mAlarmCount.get());
}
// Start an FG service -> should unblock the alarm.
startService(targetPackage, true);
waitUntil("Alarm should fire for an FG app",
() -> mAlarmCount.get() == 1);
// Try again.
mAlarmCount.set(0);
forcePackageIntoBg(targetPackage);
// Try again.
// When battery saver is enabled, alarms should be blocked.
{
final long triggerElapsed = SystemClock.elapsedRealtime() + MIN_FUTURITY;
scheduleAlarm(targetPackage, AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerElapsed,
/* whileIdle=*/ false);
ThreadUtils.sleepUntilRealtime(triggerElapsed + DEFAULT_WAIT);
assertEquals("Non-while-idle alarm should be blocked in battery saver",
0, mAlarmCount.get());
}
// This time, disable EBS -> should unblock the alarm.
enableBatterySaver(false);
waitUntil("Allow-while-idle alarm should be blocked in battery saver",
() -> mAlarmCount.get() == 1);
}
}