Merge "Integrate AlarmManager with TARE."

This commit is contained in:
Kweku Adams
2021-09-24 02:55:43 +00:00
committed by Android (Google) Code Review
5 changed files with 562 additions and 13 deletions

View File

@@ -43,7 +43,7 @@ import java.util.Date;
*/
class Alarm {
@VisibleForTesting
public static final int NUM_POLICIES = 4;
public static final int NUM_POLICIES = 5;
/**
* Index used to store the time the alarm was requested to expire. To be used with
* {@link #setPolicyElapsed(int, long)}.
@@ -66,6 +66,12 @@ class Alarm {
*/
public static final int BATTERY_SAVER_POLICY_INDEX = 3;
/**
* Index used to store the earliest time the alarm can expire based on TARE policy.
* To be used with {@link #setPolicyElapsed(int, long)}.
*/
public static final int TARE_POLICY_INDEX = 4;
/**
* Reason to use for inexact alarms.
*/
@@ -254,8 +260,10 @@ class Alarm {
return "device_idle";
case BATTERY_SAVER_POLICY_INDEX:
return "battery_saver";
case TARE_POLICY_INDEX:
return "tare";
default:
return "--unknown--";
return "--unknown(" + index + ")--";
}
}

View File

@@ -45,6 +45,7 @@ import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_COMPAT;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_NOT_APPLICABLE;
import static com.android.server.alarm.Alarm.EXACT_ALLOW_REASON_PERMISSION;
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
import static com.android.server.alarm.Alarm.TARE_POLICY_INDEX;
import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_ALARM_CANCELLED;
import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_DATA_CLEARED;
import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_REASON_EXACT_PERMISSION_REVOKED;
@@ -73,6 +74,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Binder;
@@ -112,6 +114,7 @@ import android.util.NtpTrustedTime;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseArrayMap;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
@@ -140,6 +143,8 @@ import com.android.server.SystemServiceManager;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.tare.AlarmManagerEconomicPolicy;
import com.android.server.tare.EconomyManagerInternal;
import com.android.server.usage.AppStandbyInternal;
import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
@@ -185,6 +190,7 @@ public class AlarmManagerService extends SystemService {
static final boolean DEBUG_WAKELOCK = localLOGV || false;
static final boolean DEBUG_BG_LIMIT = localLOGV || false;
static final boolean DEBUG_STANDBY = localLOGV || false;
static final boolean DEBUG_TARE = localLOGV || false;
static final boolean RECORD_ALARMS_IN_HISTORY = true;
static final boolean RECORD_DEVICE_IDLE_ALARMS = false;
static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
@@ -213,6 +219,7 @@ public class AlarmManagerService extends SystemService {
DeviceIdleInternal mLocalDeviceIdleController;
private UsageStatsManagerInternal mUsageStatsManagerInternal;
private ActivityManagerInternal mActivityManagerInternal;
private final EconomyManagerInternal mEconomyManagerInternal;
private PackageManagerInternal mPackageManagerInternal;
private volatile PermissionManagerServiceInternal mLocalPermissionManager;
@@ -230,6 +237,14 @@ public class AlarmManagerService extends SystemService {
@GuardedBy("mLock")
SparseIntArray mLastOpScheduleExactAlarm = new SparseIntArray();
/**
* Local cache of the ability of each userId-pkg to afford the various bills we're tracking for
* them.
*/
@GuardedBy("mLock")
private final SparseArrayMap<String, ArrayMap<EconomyManagerInternal.ActionBill, Boolean>>
mAffordabilityCache = new SparseArrayMap<>();
// List of alarms per uid deferred due to user applied background restrictions on the source app
SparseArray<ArrayList<Alarm>> mPendingBackgroundAlarms = new SparseArray<>();
private long mNextWakeup;
@@ -298,11 +313,13 @@ public class AlarmManagerService extends SystemService {
interface Stats {
int REORDER_ALARMS_FOR_STANDBY = 0;
int HAS_SCHEDULE_EXACT_ALARM = 1;
int REORDER_ALARMS_FOR_TARE = 2;
}
private final StatLogger mStatLogger = new StatLogger("Alarm manager stats", new String[]{
"REORDER_ALARMS_FOR_STANDBY",
"HAS_SCHEDULE_EXACT_ALARM",
"REORDER_ALARMS_FOR_TARE",
});
BroadcastOptions mOptsWithFgs = BroadcastOptions.makeBasic();
@@ -472,7 +489,8 @@ public class AlarmManagerService extends SystemService {
* holding the AlarmManagerService.mLock lock.
*/
@VisibleForTesting
final class Constants implements DeviceConfig.OnPropertiesChangedListener {
final class Constants extends ContentObserver
implements DeviceConfig.OnPropertiesChangedListener {
@VisibleForTesting
static final int MAX_EXACT_ALARM_DENY_LIST_SIZE = 250;
@@ -670,10 +688,13 @@ public class AlarmManagerService extends SystemService {
public boolean KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED =
DEFAULT_KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED;
public boolean USE_TARE_POLICY = Settings.Global.DEFAULT_ENABLE_TARE == 1;
private long mLastAllowWhileIdleWhitelistDuration = -1;
private int mVersion = 0;
Constants() {
Constants(Handler handler) {
super(handler);
updateAllowWhileIdleWhitelistDurationLocked();
for (int i = 0; i < APP_STANDBY_QUOTAS.length; i++) {
APP_STANDBY_QUOTAS[i] = DEFAULT_APP_STANDBY_QUOTAS[i];
@@ -687,8 +708,11 @@ public class AlarmManagerService extends SystemService {
}
public void start() {
mInjector.registerContentObserver(this,
Settings.Global.getUriFor(Settings.Global.ENABLE_TARE));
mInjector.registerDeviceConfigListener(this);
onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_ALARM_MANAGER));
updateTareSettings();
}
public void updateAllowWhileIdleWhitelistDurationLocked() {
@@ -860,6 +884,39 @@ public class AlarmManagerService extends SystemService {
}
}
@Override
public void onChange(boolean selfChange) {
updateTareSettings();
}
private void updateTareSettings() {
synchronized (mLock) {
final boolean isTareEnabled = Settings.Global.getInt(
getContext().getContentResolver(),
Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1;
if (USE_TARE_POLICY != isTareEnabled) {
USE_TARE_POLICY = isTareEnabled;
final boolean changed = mAlarmStore.updateAlarmDeliveries(alarm -> {
final boolean standbyChanged = adjustDeliveryTimeBasedOnBucketLocked(alarm);
final boolean tareChanged = adjustDeliveryTimeBasedOnTareLocked(alarm);
if (USE_TARE_POLICY) {
registerTareListener(alarm);
} else {
mEconomyManagerInternal.unregisterAffordabilityChangeListener(
UserHandle.getUserId(alarm.uid), alarm.sourcePackage,
mAffordabilityChangeListener,
TareBill.getAppropriateBill(alarm));
}
return standbyChanged || tareChanged;
});
if (changed) {
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
}
}
}
}
private void updateExactAlarmDenyList(String[] newDenyList) {
final Set<String> newSet = Collections.unmodifiableSet(new ArraySet<>(newDenyList));
final Set<String> removed = new ArraySet<>(EXACT_ALARM_DENY_LIST);
@@ -1059,6 +1116,9 @@ public class AlarmManagerService extends SystemService {
KILL_ON_SCHEDULE_EXACT_ALARM_REVOKED);
pw.println();
pw.print(Settings.Global.ENABLE_TARE, USE_TARE_POLICY);
pw.println();
pw.decreaseIndent();
}
@@ -1177,6 +1237,7 @@ public class AlarmManagerService extends SystemService {
AlarmManagerService(Context context, Injector injector) {
super(context);
mInjector = injector;
mEconomyManagerInternal = LocalServices.getService(EconomyManagerInternal.class);
}
public AlarmManagerService(Context context) {
@@ -1283,6 +1344,29 @@ public class AlarmManagerService extends SystemService {
return changed;
}
/**
* Recalculates alarm send times based on TARE wealth.
*
* @param targetPackages [Package, User] pairs for which alarms need to be re-evaluated,
* null indicates all
* @return True if there was any reordering done to the current list.
*/
boolean reorderAlarmsBasedOnTare(ArraySet<Pair<String, Integer>> targetPackages) {
final long start = mStatLogger.getTime();
final boolean changed = mAlarmStore.updateAlarmDeliveries(a -> {
final Pair<String, Integer> packageUser =
Pair.create(a.sourcePackage, UserHandle.getUserId(a.creatorUid));
if (targetPackages != null && !targetPackages.contains(packageUser)) {
return false;
}
return adjustDeliveryTimeBasedOnTareLocked(a);
});
mStatLogger.logDurationStat(Stats.REORDER_ALARMS_FOR_TARE, start);
return changed;
}
private boolean restoreRequestedTime(Alarm a) {
return a.setPolicyElapsed(REQUESTER_POLICY_INDEX, convertToElapsed(a.origWhen, a.type));
}
@@ -1632,7 +1716,7 @@ public class AlarmManagerService extends SystemService {
synchronized (mLock) {
mHandler = new AlarmHandler();
mConstants = new Constants();
mConstants = new Constants(mHandler);
mAlarmStore = mConstants.LAZY_BATCHING ? new LazyAlarmStore()
: new BatchingAlarmStore();
@@ -2255,7 +2339,7 @@ public class AlarmManagerService extends SystemService {
*/
private boolean adjustDeliveryTimeBasedOnBucketLocked(Alarm alarm) {
final long nowElapsed = mInjector.getElapsedRealtime();
if (isExemptFromAppStandby(alarm) || mAppStandbyParole) {
if (mConstants.USE_TARE_POLICY || isExemptFromAppStandby(alarm) || mAppStandbyParole) {
return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, nowElapsed);
}
@@ -2299,6 +2383,50 @@ public class AlarmManagerService extends SystemService {
return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, nowElapsed);
}
/**
* Adjusts the alarm's policy time for TARE.
*
* @param alarm The alarm to update.
* @return {@code true} if the actual delivery time of the given alarm was updated due to
* adjustments made in this call.
*/
private boolean adjustDeliveryTimeBasedOnTareLocked(Alarm alarm) {
final long nowElapsed = mInjector.getElapsedRealtime();
if (!mConstants.USE_TARE_POLICY
|| isExemptFromTare(alarm) || hasEnoughWealthLocked(alarm)) {
return alarm.setPolicyElapsed(TARE_POLICY_INDEX, nowElapsed);
}
// Not enough wealth. Just keep deferring indefinitely till the quota changes.
return alarm.setPolicyElapsed(TARE_POLICY_INDEX, nowElapsed + INDEFINITE_DELAY);
}
private void registerTareListener(Alarm alarm) {
if (!mConstants.USE_TARE_POLICY) {
return;
}
mEconomyManagerInternal.registerAffordabilityChangeListener(
UserHandle.getUserId(alarm.uid), alarm.sourcePackage,
mAffordabilityChangeListener, TareBill.getAppropriateBill(alarm));
}
/** Unregister the TARE listener associated with the alarm if it's no longer needed. */
private void maybeUnregisterTareListener(Alarm alarm) {
if (!mConstants.USE_TARE_POLICY) {
return;
}
final EconomyManagerInternal.ActionBill bill = TareBill.getAppropriateBill(alarm);
final Predicate<Alarm> isSameAlarmTypeForSameApp = (a) ->
alarm.creatorUid == a.creatorUid
&& alarm.sourcePackage.equals(a.sourcePackage)
&& bill.equals(TareBill.getAppropriateBill(a));
if (mAlarmStore.getCount(isSameAlarmTypeForSameApp) == 0) {
mEconomyManagerInternal.unregisterAffordabilityChangeListener(
UserHandle.getUserId(alarm.uid), alarm.sourcePackage,
mAffordabilityChangeListener, bill);
}
}
private void setImplLocked(Alarm a) {
if ((a.flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) {
adjustIdleUntilTime(a);
@@ -2345,6 +2473,8 @@ public class AlarmManagerService extends SystemService {
}
adjustDeliveryTimeBasedOnBatterySaver(a);
adjustDeliveryTimeBasedOnBucketLocked(a);
adjustDeliveryTimeBasedOnTareLocked(a);
registerTareListener(a);
mAlarmStore.add(a);
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
@@ -2711,14 +2841,43 @@ public class AlarmManagerService extends SystemService {
mConstants.dump(pw);
pw.println();
if (mAppStateTracker != null) {
mAppStateTracker.dump(pw);
if (mConstants.USE_TARE_POLICY) {
pw.println("TARE details:");
pw.increaseIndent();
pw.println("Affordability cache:");
pw.increaseIndent();
mAffordabilityCache.forEach((userId, pkgName, billMap) -> {
final int numBills = billMap.size();
if (numBills > 0) {
pw.print(userId);
pw.print(":");
pw.print(pkgName);
pw.println(":");
pw.increaseIndent();
for (int i = 0; i < numBills; ++i) {
pw.print(TareBill.getName(billMap.keyAt(i)));
pw.print(": ");
pw.println(billMap.valueAt(i));
}
pw.decreaseIndent();
}
});
pw.decreaseIndent();
pw.decreaseIndent();
pw.println();
} else {
if (mAppStateTracker != null) {
mAppStateTracker.dump(pw);
pw.println();
}
pw.println("App Standby Parole: " + mAppStandbyParole);
pw.println();
}
pw.println("App Standby Parole: " + mAppStandbyParole);
pw.println();
final long nowELAPSED = mInjector.getElapsedRealtime();
final long nowUPTIME = SystemClock.uptimeMillis();
final long nowRTC = mInjector.getCurrentTimeMillis();
@@ -3673,6 +3832,7 @@ public class AlarmManagerService extends SystemService {
mRemovalHistory.put(removed.uid, bufferForUid);
}
bufferForUid.append(new RemovedAlarm(removed, reason, nowRtc, nowElapsed));
maybeUnregisterTareListener(removed);
}
if (removedFromStore) {
@@ -4010,6 +4170,11 @@ public class AlarmManagerService extends SystemService {
alarm.uid, alarm.statsTag);
}
mDeliveryTracker.deliverLocked(alarm, nowELAPSED);
reportAlarmEventToTare(alarm);
if (alarm.repeatInterval <= 0) {
// Don't bother trying to unregister for a repeating alarm.
maybeUnregisterTareListener(alarm);
}
} catch (RuntimeException e) {
Slog.w(TAG, "Failure sending alarm.", e);
}
@@ -4018,12 +4183,60 @@ public class AlarmManagerService extends SystemService {
}
}
private void reportAlarmEventToTare(Alarm alarm) {
final boolean allowWhileIdle =
(alarm.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_ALLOW_WHILE_IDLE)) != 0;
final int action;
if (alarm.alarmClock != null) {
action = AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK;
} else if (alarm.wakeup) {
if (alarm.windowLength == 0) {
if (allowWhileIdle) {
action = AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE;
} else {
action = AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT;
}
} else {
if (allowWhileIdle) {
action =
AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE;
} else {
action = AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT;
}
}
} else {
if (alarm.windowLength == 0) {
if (allowWhileIdle) {
action = AlarmManagerEconomicPolicy
.ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE;
} else {
action = AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT;
}
} else {
if (allowWhileIdle) {
action = AlarmManagerEconomicPolicy
.ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE;
} else {
action = AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT;
}
}
}
mEconomyManagerInternal.noteInstantaneousEvent(
UserHandle.getUserId(alarm.uid), alarm.sourcePackage, action, null);
}
@VisibleForTesting
static boolean isExemptFromAppStandby(Alarm a) {
return a.alarmClock != null || UserHandle.isCore(a.creatorUid)
|| (a.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_ALLOW_WHILE_IDLE)) != 0;
}
@VisibleForTesting
static boolean isExemptFromTare(Alarm a) {
return a.alarmClock != null || UserHandle.isCore(a.creatorUid)
|| (a.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)) != 0;
}
@VisibleForTesting
static class Injector {
private long mNativeData;
@@ -4117,6 +4330,10 @@ public class AlarmManagerService extends SystemService {
return service.new ClockReceiver();
}
void registerContentObserver(ContentObserver contentObserver, Uri uri) {
mContext.getContentResolver().registerContentObserver(uri, false, contentObserver);
}
void registerDeviceConfigListener(DeviceConfig.OnPropertiesChangedListener listener) {
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ALARM_MANAGER,
JobSchedulerBackgroundThread.getExecutor(), listener);
@@ -4249,13 +4466,23 @@ public class AlarmManagerService extends SystemService {
new ArraySet<>();
for (int i = 0; i < triggerList.size(); i++) {
final Alarm a = triggerList.get(i);
if (!isExemptFromAppStandby(a)) {
if (mConstants.USE_TARE_POLICY) {
if (!isExemptFromTare(a)) {
triggerPackages.add(Pair.create(
a.sourcePackage,
UserHandle.getUserId(a.creatorUid)));
}
} else if (!isExemptFromAppStandby(a)) {
triggerPackages.add(Pair.create(
a.sourcePackage, UserHandle.getUserId(a.creatorUid)));
}
}
deliverAlarmsLocked(triggerList, nowELAPSED);
reorderAlarmsBasedOnStandbyBuckets(triggerPackages);
if (mConstants.USE_TARE_POLICY) {
reorderAlarmsBasedOnTare(triggerPackages);
} else {
reorderAlarmsBasedOnStandbyBuckets(triggerPackages);
}
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
MetricsHelper.pushAlarmBatchDelivered(triggerList.size(), wakeUps);
@@ -4307,6 +4534,31 @@ public class AlarmManagerService extends SystemService {
return alarm.creatorUid;
}
@GuardedBy("mLock")
private boolean canAffordBillLocked(@NonNull Alarm alarm,
@NonNull EconomyManagerInternal.ActionBill bill) {
final int userId = UserHandle.getUserId(alarm.uid);
final String pkgName = alarm.sourcePackage;
ArrayMap<EconomyManagerInternal.ActionBill, Boolean> actionAffordability =
mAffordabilityCache.get(userId, pkgName);
if (actionAffordability == null) {
actionAffordability = new ArrayMap<>();
mAffordabilityCache.add(userId, pkgName, actionAffordability);
}
if (actionAffordability.containsKey(bill)) {
return actionAffordability.get(bill);
}
final boolean canAfford = mEconomyManagerInternal.canPayFor(userId, pkgName, bill);
actionAffordability.put(bill, canAfford);
return canAfford;
}
@GuardedBy("mLock")
private boolean hasEnoughWealthLocked(@NonNull Alarm alarm) {
return canAffordBillLocked(alarm, TareBill.getAppropriateBill(alarm));
}
@VisibleForTesting
class AlarmHandler extends Handler {
@@ -4321,6 +4573,7 @@ public class AlarmManagerService extends SystemService {
public static final int EXACT_ALARM_DENY_LIST_PACKAGES_ADDED = 9;
public static final int EXACT_ALARM_DENY_LIST_PACKAGES_REMOVED = 10;
public static final int REFRESH_EXACT_ALARM_CANDIDATES = 11;
public static final int TARE_AFFORDABILITY_CHANGED = 12;
AlarmHandler() {
super(Looper.myLooper());
@@ -4402,6 +4655,20 @@ public class AlarmManagerService extends SystemService {
}
break;
case TARE_AFFORDABILITY_CHANGED:
synchronized (mLock) {
final int userId = msg.arg1;
final String packageName = (String) msg.obj;
final ArraySet<Pair<String, Integer>> filterPackages = new ArraySet<>();
filterPackages.add(Pair.create(packageName, userId));
if (reorderAlarmsBasedOnTare(filterPackages)) {
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
}
}
break;
case REMOVE_FOR_CANCELED:
final PendingIntent operation = (PendingIntent) msg.obj;
synchronized (mLock) {
@@ -4656,6 +4923,31 @@ public class AlarmManagerService extends SystemService {
}
}
private final EconomyManagerInternal.AffordabilityChangeListener mAffordabilityChangeListener =
new EconomyManagerInternal.AffordabilityChangeListener() {
@Override
public void onAffordabilityChanged(int userId, @NonNull String packageName,
@NonNull EconomyManagerInternal.ActionBill bill, boolean canAfford) {
if (DEBUG_TARE) {
Slog.d(TAG,
userId + ":" + packageName + " affordability for "
+ TareBill.getName(bill) + " changed to " + canAfford);
}
ArrayMap<EconomyManagerInternal.ActionBill, Boolean> actionAffordability =
mAffordabilityCache.get(userId, packageName);
if (actionAffordability == null) {
actionAffordability = new ArrayMap<>();
mAffordabilityCache.add(userId, packageName, actionAffordability);
}
actionAffordability.put(bill, canAfford);
mHandler.obtainMessage(AlarmHandler.TARE_AFFORDABILITY_CHANGED, userId,
canAfford ? 1 : 0, packageName)
.sendToTarget();
}
};
private final Listener mForceAppStandbyListener = new Listener() {
@Override

View File

@@ -0,0 +1,144 @@
/*
* Copyright (C) 2021 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 com.android.server.alarm;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE;
import static android.app.AlarmManager.FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED;
import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_CLOCK;
import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT;
import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE;
import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT;
import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE;
import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT;
import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE;
import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT;
import static com.android.server.tare.AlarmManagerEconomicPolicy.ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE;
import android.annotation.NonNull;
import com.android.server.tare.EconomyManagerInternal;
import com.android.server.tare.EconomyManagerInternal.ActionBill;
import java.util.List;
/**
* Container to maintain alarm TARE {@link ActionBill}s and their related methods.
*/
final class TareBill {
/**
* Bill to use for AlarmClocks.
*/
static final ActionBill ALARM_CLOCK = new ActionBill(List.of(
new EconomyManagerInternal.AnticipatedAction(ACTION_ALARM_CLOCK, 1, 0)));
/**
* Bills to use for various alarm types.
*/
static final ActionBill NONWAKEUP_INEXACT_ALARM = new ActionBill(List.of(
new EconomyManagerInternal.AnticipatedAction(ACTION_ALARM_NONWAKEUP_INEXACT, 1, 0)));
static final ActionBill NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM = new ActionBill(List.of(
new EconomyManagerInternal.AnticipatedAction(
ACTION_ALARM_NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE, 1, 0)));
static final ActionBill NONWAKEUP_EXACT_ALARM = new ActionBill(List.of(
new EconomyManagerInternal.AnticipatedAction(ACTION_ALARM_NONWAKEUP_EXACT, 1, 0)));
static final ActionBill NONWAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM = new ActionBill(List.of(
new EconomyManagerInternal.AnticipatedAction(
ACTION_ALARM_NONWAKEUP_EXACT_ALLOW_WHILE_IDLE, 1, 0)));
static final ActionBill WAKEUP_INEXACT_ALARM = new ActionBill(List.of(
new EconomyManagerInternal.AnticipatedAction(ACTION_ALARM_WAKEUP_INEXACT, 1, 0)));
static final ActionBill WAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM = new ActionBill(List.of(
new EconomyManagerInternal.AnticipatedAction(
ACTION_ALARM_WAKEUP_INEXACT_ALLOW_WHILE_IDLE, 1, 0)));
static final ActionBill WAKEUP_EXACT_ALARM = new ActionBill(List.of(
new EconomyManagerInternal.AnticipatedAction(ACTION_ALARM_WAKEUP_EXACT, 1, 0)));
static final ActionBill WAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM = new ActionBill(List.of(
new EconomyManagerInternal.AnticipatedAction(
ACTION_ALARM_WAKEUP_EXACT_ALLOW_WHILE_IDLE, 1, 0)));
@NonNull
static ActionBill getAppropriateBill(@NonNull Alarm alarm) {
if (alarm.alarmClock != null) {
return ALARM_CLOCK;
}
final boolean allowWhileIdle =
(alarm.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_ALLOW_WHILE_IDLE)) != 0;
final boolean isExact = alarm.windowLength == 0;
if (alarm.wakeup) {
if (isExact) {
if (allowWhileIdle) {
return WAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM;
}
return WAKEUP_EXACT_ALARM;
}
// Inexact
if (allowWhileIdle) {
return WAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM;
}
return WAKEUP_INEXACT_ALARM;
}
// Nonwakeup
if (isExact) {
if (allowWhileIdle) {
return NONWAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM;
}
return NONWAKEUP_EXACT_ALARM;
}
if (allowWhileIdle) {
return NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM;
}
return NONWAKEUP_INEXACT_ALARM;
}
@NonNull
static String getName(@NonNull ActionBill bill) {
if (bill.equals(ALARM_CLOCK)) {
return "ALARM_CLOCK_BILL";
}
if (bill.equals(NONWAKEUP_INEXACT_ALARM)) {
return "NONWAKEUP_INEXACT_ALARM_BILL";
}
if (bill.equals(NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM)) {
return "NONWAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM_BILL";
}
if (bill.equals(NONWAKEUP_EXACT_ALARM)) {
return "NONWAKEUP_EXACT_ALARM_BILL";
}
if (bill.equals(NONWAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM)) {
return "NONWAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM_BILL";
}
if (bill.equals(WAKEUP_INEXACT_ALARM)) {
return "WAKEUP_INEXACT_ALARM_BILL";
}
if (bill.equals(WAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM)) {
return "WAKEUP_INEXACT_ALLOW_WHILE_IDLE_ALARM_BILL";
}
if (bill.equals(WAKEUP_EXACT_ALARM)) {
return "WAKEUP_EXACT_ALARM_BILL";
}
if (bill.equals(WAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM)) {
return "WAKEUP_EXACT_ALLOW_WHILE_IDLE_ALARM_BILL";
}
return "UNKNOWN_BILL (" + bill.toString() + ")";
}
private TareBill() {
}
}

View File

@@ -63,6 +63,7 @@ import static com.android.server.alarm.AlarmManagerService.AlarmHandler.EXACT_AL
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REFRESH_EXACT_ALARM_CANDIDATES;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_EXACT_ALARMS;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.REMOVE_FOR_CANCELED;
import static com.android.server.alarm.AlarmManagerService.AlarmHandler.TARE_AFFORDABILITY_CHANGED;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_QUOTA;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_COMPAT_WINDOW;
import static com.android.server.alarm.AlarmManagerService.Constants.KEY_ALLOW_WHILE_IDLE_QUOTA;
@@ -120,9 +121,12 @@ import android.app.IAlarmManager;
import android.app.PendingIntent;
import android.app.compat.CompatChanges;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManagerInternal;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.Handler;
@@ -138,6 +142,7 @@ import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.text.format.DateFormat;
import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
@@ -158,6 +163,7 @@ import com.android.server.SystemService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.tare.EconomyManagerInternal;
import com.android.server.usage.AppStandbyInternal;
import libcore.util.EmptyArray;
@@ -201,6 +207,8 @@ public class AlarmManagerServiceTest {
@Mock
private Context mMockContext;
@Mock
private ContentResolver mContentResolver;
@Mock
private IActivityManager mIActivityManager;
@Mock
private IAppOpsService mIAppOpsService;
@@ -225,6 +233,8 @@ public class AlarmManagerServiceTest {
@Mock
private PowerManager.WakeLock mWakeLock;
@Mock
private EconomyManagerInternal mEconomyManagerInternal;
@Mock
DeviceConfig.Properties mDeviceConfigProperties;
HashSet<String> mDeviceConfigKeys = new HashSet<>();
@@ -349,6 +359,11 @@ public class AlarmManagerServiceTest {
return mWakeLock;
}
@Override
void registerContentObserver(ContentObserver observer, Uri uri) {
// Do nothing.
}
@Override
void registerDeviceConfigListener(DeviceConfig.OnPropertiesChangedListener listener) {
// Do nothing.
@@ -369,6 +384,7 @@ public class AlarmManagerServiceTest {
.initMocks(this)
.spyStatic(ActivityManager.class)
.mockStatic(CompatChanges.class)
.spyStatic(DateFormat.class)
.spyStatic(DeviceConfig.class)
.mockStatic(LocalServices.class)
.spyStatic(Looper.class)
@@ -384,6 +400,8 @@ public class AlarmManagerServiceTest {
doReturn(mIActivityManager).when(ActivityManager::getService);
doReturn(mDeviceIdleInternal).when(
() -> LocalServices.getService(DeviceIdleInternal.class));
doReturn(mEconomyManagerInternal).when(
() -> LocalServices.getService(EconomyManagerInternal.class));
doReturn(mPermissionManagerInternal).when(
() -> LocalServices.getService(PermissionManagerServiceInternal.class));
doReturn(mActivityManagerInternal).when(
@@ -403,6 +421,8 @@ public class AlarmManagerServiceTest {
eq(TEST_CALLING_USER), anyLong())).thenReturn(STANDBY_BUCKET_ACTIVE);
doReturn(Looper.getMainLooper()).when(Looper::myLooper);
when(mMockContext.getContentResolver()).thenReturn(mContentResolver);
doReturn(mDeviceConfigKeys).when(mDeviceConfigProperties).getKeyset();
when(mDeviceConfigProperties.getLong(anyString(), anyLong()))
.thenAnswer((Answer<Long>) invocationOnMock -> {
@@ -421,6 +441,9 @@ public class AlarmManagerServiceTest {
doReturn(mDeviceConfigProperties).when(
() -> DeviceConfig.getProperties(
eq(DeviceConfig.NAMESPACE_ALARM_MANAGER), ArgumentMatchers.<String>any()));
// Needed to ensure logging doesn't cause tests to fail.
doReturn(true)
.when(() -> DateFormat.is24HourFormat(eq(mMockContext), anyInt()));
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
@@ -447,6 +470,7 @@ public class AlarmManagerServiceTest {
// Other boot phases don't matter
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
setTareEnabled(false);
mAppStandbyWindow = mService.mConstants.APP_STANDBY_WINDOW;
mAllowWhileIdleWindow = mService.mConstants.ALLOW_WHILE_IDLE_WINDOW;
ArgumentCaptor<AppStandbyInternal.AppIdleStateChangeListener> captor =
@@ -596,6 +620,15 @@ public class AlarmManagerServiceTest {
mService.mConstants.onPropertiesChanged(mDeviceConfigProperties);
}
private void setTareEnabled(boolean enabled) {
doReturn(enabled ? 1 : 0).when(
() -> Settings.Global.getInt(mContentResolver, Settings.Global.ENABLE_TARE));
doReturn(enabled ? 1 : 0).when(
() -> Settings.Global.getInt(mContentResolver,
Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE));
mService.mConstants.onChange(true);
}
/**
* Lowers quotas to make testing feasible. Careful while calling as this will replace any
* existing settings for the calling test.
@@ -1987,6 +2020,44 @@ public class AlarmManagerServiceTest {
assertEquals(idleUntil, mTestTimer.getElapsed());
}
@Test
public void tareThrottling() {
setTareEnabled(true);
final ArgumentCaptor<EconomyManagerInternal.AffordabilityChangeListener> listenerCaptor =
ArgumentCaptor.forClass(EconomyManagerInternal.AffordabilityChangeListener.class);
final ArgumentCaptor<EconomyManagerInternal.ActionBill> billCaptor =
ArgumentCaptor.forClass(EconomyManagerInternal.ActionBill.class);
when(mEconomyManagerInternal
.canPayFor(eq(TEST_CALLING_USER), eq(TEST_CALLING_PACKAGE), billCaptor.capture()))
.thenReturn(false);
final PendingIntent alarmPi = getNewMockPendingIntent();
setTestAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 15, alarmPi);
assertEquals(mNowElapsedTest + INDEFINITE_DELAY, mTestTimer.getElapsed());
final EconomyManagerInternal.ActionBill bill = billCaptor.getValue();
verify(mEconomyManagerInternal).registerAffordabilityChangeListener(
eq(TEST_CALLING_USER), eq(TEST_CALLING_PACKAGE),
listenerCaptor.capture(), eq(bill));
final EconomyManagerInternal.AffordabilityChangeListener listener =
listenerCaptor.getValue();
when(mEconomyManagerInternal
.canPayFor(eq(TEST_CALLING_USER), eq(TEST_CALLING_PACKAGE), eq(bill)))
.thenReturn(true);
listener.onAffordabilityChanged(TEST_CALLING_USER, TEST_CALLING_PACKAGE, bill, true);
assertAndHandleMessageSync(TARE_AFFORDABILITY_CHANGED);
assertEquals(mNowElapsedTest + 15, mTestTimer.getElapsed());
when(mEconomyManagerInternal
.canPayFor(eq(TEST_CALLING_USER), eq(TEST_CALLING_PACKAGE), eq(bill)))
.thenReturn(false);
listener.onAffordabilityChanged(TEST_CALLING_USER, TEST_CALLING_PACKAGE, bill, false);
assertAndHandleMessageSync(TARE_AFFORDABILITY_CHANGED);
assertEquals(mNowElapsedTest + INDEFINITE_DELAY, mTestTimer.getElapsed());
}
@Test
public void dispatchOrder() throws Exception {
setDeviceConfigLong(KEY_MAX_DEVICE_IDLE_FUZZ, 0);
@@ -3065,6 +3136,23 @@ public class AlarmManagerServiceTest {
verify(() -> MetricsHelper.pushAlarmBatchDelivered(10, 5));
}
@Test
public void tareEventPushed() throws Exception {
setTareEnabled(true);
for (int i = 0; i < 10; i++) {
final int type = (i % 2 == 1) ? ELAPSED_REALTIME : ELAPSED_REALTIME_WAKEUP;
setTestAlarm(type, mNowElapsedTest + i, getNewMockPendingIntent());
}
final ArrayList<Alarm> alarms = mService.mAlarmStore.remove((alarm) -> {
return alarm.creatorUid == TEST_CALLING_UID;
});
mService.deliverAlarmsLocked(alarms, mNowElapsedTest);
verify(mEconomyManagerInternal, times(10)).noteInstantaneousEvent(
eq(TEST_CALLING_USER), eq(TEST_CALLING_PACKAGE), anyInt(), any());
}
@Test
public void setTimeZoneImpl() {
final long durationMs = 20000L;

View File

@@ -28,6 +28,7 @@ import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX;
import static com.android.server.alarm.Alarm.NUM_POLICIES;
import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX;
import static com.android.server.alarm.AlarmManagerService.isExemptFromAppStandby;
import static com.android.server.alarm.AlarmManagerService.isExemptFromTare;
import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE;
import static com.android.server.alarm.Constants.TEST_CALLING_UID;
@@ -193,4 +194,20 @@ public class AlarmTest {
createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)));
assertTrue("Alarm clock not exempt", isExemptFromAppStandby(createAlarmClock(anything)));
}
@Test
public void testIsExemptFromTare() {
final long anything = 54321; // Arbitrary number, doesn't matter for this test.
assertFalse("Basic alarm exempt", isExemptFromTare(
createDefaultAlarm(anything, anything, 0)));
assertFalse("FLAG_ALLOW_WHILE_IDLE_COMPAT exempt", isExemptFromTare(
createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE_COMPAT)));
assertFalse("ALLOW_WHILE_IDLE exempt", isExemptFromTare(
createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE)));
assertTrue("ALLOW_WHILE_IDLE_UNRESTRICTED not exempt", isExemptFromTare(
createDefaultAlarm(anything, anything, FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED)));
assertTrue("Alarm clock not exempt", isExemptFromTare(createAlarmClock(anything)));
}
}