Loading apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +7 −0 Original line number Diff line number Diff line Loading @@ -71,6 +71,13 @@ public interface AppStandbyInternal { long getTimeSinceLastJobRun(String packageName, int userId); /** * Returns the time (in milliseconds) since the app was last interacted with by the user. * This can be larger than the current elapsedRealtime, in case it happened before boot or * a really large value if the app was never interacted with. */ long getTimeSinceLastUsedByUser(String packageName, int userId); void onUserRemoved(int userId); void addListener(AppIdleStateChangeListener listener); Loading apex/jobscheduler/service/java/com/android/server/tare/Agent.java +52 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static com.android.server.tare.EconomicPolicy.REGULATION_BASIC_INCOME; import static com.android.server.tare.EconomicPolicy.REGULATION_BIRTHRIGHT; import static com.android.server.tare.EconomicPolicy.REGULATION_WEALTH_RECLAMATION; import static com.android.server.tare.EconomicPolicy.TYPE_ACTION; import static com.android.server.tare.EconomicPolicy.TYPE_REWARD; import static com.android.server.tare.EconomicPolicy.eventToString; Loading @@ -46,6 +47,7 @@ import android.util.SparseArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; import com.android.server.usage.AppStandbyInternal; import java.util.List; import java.util.Objects; Loading @@ -53,7 +55,7 @@ import java.util.PriorityQueue; /** * Other half of the IRS. The agent handles the nitty gritty details, interacting directly with * ledgers, carrying out specific events such as tax collection and granting initial balances or * ledgers, carrying out specific events such as wealth reclamation, granting initial balances or * replenishing balances, and tracking ongoing events. */ class Agent { Loading @@ -61,6 +63,11 @@ class Agent { private static final boolean DEBUG = InternalResourceService.DEBUG || Log.isLoggable(TAG, Log.DEBUG); /** * The minimum amount of time an app must not have been used by the user before we start * regularly reclaiming ARCs from it. */ private static final long MIN_UNUSED_TIME_MS = 3 * 24 * HOUR_IN_MILLIS; /** * The maximum amount of time we'll keep a transaction around for. * For now, only keep transactions we actually have a use for. We can increase it if we want Loading @@ -75,6 +82,8 @@ class Agent { private final Handler mHandler; private final InternalResourceService mIrs; private final AppStandbyInternal mAppStandbyInternal; @GuardedBy("mLock") private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>(); Loading @@ -97,6 +106,7 @@ class Agent { mIrs = irs; mCompleteEconomicPolicy = completeEconomicPolicy; mHandler = new AgentHandler(TareHandlerThread.get().getLooper()); mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class); } @GuardedBy("mLock") Loading Loading @@ -198,6 +208,47 @@ class Agent { } } /** * Reclaim a percentage of unused ARCs from every app that hasn't been used recently. The * reclamation will not reduce an app's balance below its minimum balance as dictated by the * EconomicPolicy. * * @param percentage A value between 0 and 1 to indicate how much of the unused balance should * be reclaimed. */ @GuardedBy("mLock") void reclaimUnusedAssetsLocked(double percentage) { final List<PackageInfo> pkgs = mIrs.getInstalledPackages(); final long now = System.currentTimeMillis(); for (int i = 0; i < pkgs.size(); ++i) { final int userId = UserHandle.getUserId(pkgs.get(i).applicationInfo.uid); final String pkgName = pkgs.get(i).packageName; final Ledger ledger = getLedgerLocked(userId, pkgName); // AppStandby only counts elapsed time for things like this // TODO: should we use clock time instead? final long timeSinceLastUsedMs = mAppStandbyInternal.getTimeSinceLastUsedByUser(pkgName, userId); if (timeSinceLastUsedMs >= MIN_UNUSED_TIME_MS) { // Use a constant floor instead of the scaled floor from the IRS. final long minBalance = mCompleteEconomicPolicy.getMinSatiatedBalance(userId, pkgName); final long curBalance = ledger.getCurrentBalance(); long toReclaim = (long) (curBalance * percentage); if (curBalance - toReclaim < minBalance) { toReclaim = curBalance - minBalance; } if (toReclaim > 0) { Slog.i(TAG, "Reclaiming unused wealth! Taking " + toReclaim + " from <" + userId + ">" + pkgName); recordTransactionLocked(userId, pkgName, ledger, new Ledger.Transaction( now, now, REGULATION_WEALTH_RECLAMATION, null, -toReclaim)); } } } } @GuardedBy("mLock") void distributeBasicIncomeLocked(int batteryLevel) { List<PackageInfo> pkgs = mIrs.getInstalledPackages(); Loading apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java +3 −3 Original line number Diff line number Diff line Loading @@ -55,7 +55,7 @@ public abstract class EconomicPolicy { static final int REGULATION_BASIC_INCOME = TYPE_REGULATION | 0; static final int REGULATION_BIRTHRIGHT = TYPE_REGULATION | 1; static final int REGULATION_TAX = TYPE_REGULATION | 2; static final int REGULATION_WEALTH_RECLAMATION = TYPE_REGULATION | 2; static final int REWARD_NOTIFICATION_SEEN = TYPE_REWARD | 0; static final int REWARD_NOTIFICATION_INTERACTION = TYPE_REWARD | 1; Loading Loading @@ -347,8 +347,8 @@ public abstract class EconomicPolicy { return "BASIC_INCOME"; case REGULATION_BIRTHRIGHT: return "BIRTHRIGHT"; case REGULATION_TAX: return "TAX"; case REGULATION_WEALTH_RECLAMATION: return "WEALTH_RECLAMATION"; } return "UNKNOWN_REGULATION:" + Integer.toHexString(eventId); } Loading apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java +55 −0 Original line number Diff line number Diff line Loading @@ -16,8 +16,12 @@ package com.android.server.tare; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; Loading @@ -29,6 +33,7 @@ import android.os.BatteryManagerInternal; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.os.UserHandle; import android.util.ArraySet; import android.util.Log; Loading Loading @@ -57,6 +62,10 @@ public class InternalResourceService extends SystemService { public static final String TAG = "TARE-IRS"; public static final boolean DEBUG = Log.isLoggable("TARE", Log.DEBUG); static final long UNUSED_RECLAMATION_PERIOD_MS = 24 * HOUR_IN_MILLIS; /** How much of an app's unused wealth should be reclaimed periodically. */ private static final float DEFAULT_UNUSED_RECLAMATION_PERCENTAGE = .1f; /** Global local for all resource economy state. */ private final Object mLock = new Object(); Loading @@ -83,6 +92,9 @@ public class InternalResourceService extends SystemService { // In the range [0,100] to represent 0% to 100% battery. @GuardedBy("mLock") private int mCurrentBatteryLevel; // TODO: load from disk @GuardedBy("mLock") private long mLastUnusedReclamationTime; @SuppressWarnings("FieldCanBeLocal") private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { Loading Loading @@ -133,7 +145,21 @@ public class InternalResourceService extends SystemService { } }; private final AlarmManager.OnAlarmListener mUnusedWealthReclamationListener = new AlarmManager.OnAlarmListener() { @Override public void onAlarm() { synchronized (mLock) { mAgent.reclaimUnusedAssetsLocked(DEFAULT_UNUSED_RECLAMATION_PERCENTAGE); mLastUnusedReclamationTime = System.currentTimeMillis(); scheduleUnusedWealthReclamationLocked(); } } }; private static final int MSG_NOTIFY_BALANCE_CHANGE_LISTENERS = 0; private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1; private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*"; /** * Initializes the system service. Loading Loading @@ -186,6 +212,8 @@ public class InternalResourceService extends SystemService { } else { mIsSetup = true; } scheduleUnusedWealthReclamationLocked(); mCompleteEconomicPolicy.onSystemServicesReady(); } } } Loading Loading @@ -314,6 +342,26 @@ public class InternalResourceService extends SystemService { .sendToTarget(); } @GuardedBy("mLock") private void scheduleUnusedWealthReclamationLocked() { final long now = System.currentTimeMillis(); final long nextReclamationTime = Math.max(mLastUnusedReclamationTime + UNUSED_RECLAMATION_PERIOD_MS, now + 30_000); mHandler.post(() -> { // Never call out to AlarmManager with the lock held. This sits below AM. AlarmManager alarmManager = getContext().getSystemService(AlarmManager.class); if (alarmManager != null) { alarmManager.setWindow(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + (nextReclamationTime - now), 30 * MINUTE_IN_MILLIS, ALARM_TAG_WEALTH_RECLAMATION, mUnusedWealthReclamationListener, mHandler); } else { mHandler.sendEmptyMessageDelayed( MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT, 30_000); } }); } private int getCurrentBatteryLevel() { return mBatteryManagerInternal.getBatteryLevel(); } Loading Loading @@ -351,6 +399,13 @@ public class InternalResourceService extends SystemService { } } break; case MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT: removeMessages(MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT); synchronized (mLock) { scheduleUnusedWealthReclamationLocked(); } break; } } } Loading apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java +11 −0 Original line number Diff line number Diff line Loading @@ -462,6 +462,17 @@ public class AppIdleHistory { return getElapsedTime(elapsedRealtime) - appUsageHistory.lastJobRunTime; } public long getTimeSinceLastUsedByUser(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, false); if (appUsageHistory == null || appUsageHistory.lastUsedByUserElapsedTime == Long.MIN_VALUE || appUsageHistory.lastUsedByUserElapsedTime == 0) { return Long.MAX_VALUE; } return getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedByUserElapsedTime; } public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = Loading Loading
apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +7 −0 Original line number Diff line number Diff line Loading @@ -71,6 +71,13 @@ public interface AppStandbyInternal { long getTimeSinceLastJobRun(String packageName, int userId); /** * Returns the time (in milliseconds) since the app was last interacted with by the user. * This can be larger than the current elapsedRealtime, in case it happened before boot or * a really large value if the app was never interacted with. */ long getTimeSinceLastUsedByUser(String packageName, int userId); void onUserRemoved(int userId); void addListener(AppIdleStateChangeListener listener); Loading
apex/jobscheduler/service/java/com/android/server/tare/Agent.java +52 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static com.android.server.tare.EconomicPolicy.REGULATION_BASIC_INCOME; import static com.android.server.tare.EconomicPolicy.REGULATION_BIRTHRIGHT; import static com.android.server.tare.EconomicPolicy.REGULATION_WEALTH_RECLAMATION; import static com.android.server.tare.EconomicPolicy.TYPE_ACTION; import static com.android.server.tare.EconomicPolicy.TYPE_REWARD; import static com.android.server.tare.EconomicPolicy.eventToString; Loading @@ -46,6 +47,7 @@ import android.util.SparseArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; import com.android.server.usage.AppStandbyInternal; import java.util.List; import java.util.Objects; Loading @@ -53,7 +55,7 @@ import java.util.PriorityQueue; /** * Other half of the IRS. The agent handles the nitty gritty details, interacting directly with * ledgers, carrying out specific events such as tax collection and granting initial balances or * ledgers, carrying out specific events such as wealth reclamation, granting initial balances or * replenishing balances, and tracking ongoing events. */ class Agent { Loading @@ -61,6 +63,11 @@ class Agent { private static final boolean DEBUG = InternalResourceService.DEBUG || Log.isLoggable(TAG, Log.DEBUG); /** * The minimum amount of time an app must not have been used by the user before we start * regularly reclaiming ARCs from it. */ private static final long MIN_UNUSED_TIME_MS = 3 * 24 * HOUR_IN_MILLIS; /** * The maximum amount of time we'll keep a transaction around for. * For now, only keep transactions we actually have a use for. We can increase it if we want Loading @@ -75,6 +82,8 @@ class Agent { private final Handler mHandler; private final InternalResourceService mIrs; private final AppStandbyInternal mAppStandbyInternal; @GuardedBy("mLock") private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>(); Loading @@ -97,6 +106,7 @@ class Agent { mIrs = irs; mCompleteEconomicPolicy = completeEconomicPolicy; mHandler = new AgentHandler(TareHandlerThread.get().getLooper()); mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class); } @GuardedBy("mLock") Loading Loading @@ -198,6 +208,47 @@ class Agent { } } /** * Reclaim a percentage of unused ARCs from every app that hasn't been used recently. The * reclamation will not reduce an app's balance below its minimum balance as dictated by the * EconomicPolicy. * * @param percentage A value between 0 and 1 to indicate how much of the unused balance should * be reclaimed. */ @GuardedBy("mLock") void reclaimUnusedAssetsLocked(double percentage) { final List<PackageInfo> pkgs = mIrs.getInstalledPackages(); final long now = System.currentTimeMillis(); for (int i = 0; i < pkgs.size(); ++i) { final int userId = UserHandle.getUserId(pkgs.get(i).applicationInfo.uid); final String pkgName = pkgs.get(i).packageName; final Ledger ledger = getLedgerLocked(userId, pkgName); // AppStandby only counts elapsed time for things like this // TODO: should we use clock time instead? final long timeSinceLastUsedMs = mAppStandbyInternal.getTimeSinceLastUsedByUser(pkgName, userId); if (timeSinceLastUsedMs >= MIN_UNUSED_TIME_MS) { // Use a constant floor instead of the scaled floor from the IRS. final long minBalance = mCompleteEconomicPolicy.getMinSatiatedBalance(userId, pkgName); final long curBalance = ledger.getCurrentBalance(); long toReclaim = (long) (curBalance * percentage); if (curBalance - toReclaim < minBalance) { toReclaim = curBalance - minBalance; } if (toReclaim > 0) { Slog.i(TAG, "Reclaiming unused wealth! Taking " + toReclaim + " from <" + userId + ">" + pkgName); recordTransactionLocked(userId, pkgName, ledger, new Ledger.Transaction( now, now, REGULATION_WEALTH_RECLAMATION, null, -toReclaim)); } } } } @GuardedBy("mLock") void distributeBasicIncomeLocked(int batteryLevel) { List<PackageInfo> pkgs = mIrs.getInstalledPackages(); Loading
apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java +3 −3 Original line number Diff line number Diff line Loading @@ -55,7 +55,7 @@ public abstract class EconomicPolicy { static final int REGULATION_BASIC_INCOME = TYPE_REGULATION | 0; static final int REGULATION_BIRTHRIGHT = TYPE_REGULATION | 1; static final int REGULATION_TAX = TYPE_REGULATION | 2; static final int REGULATION_WEALTH_RECLAMATION = TYPE_REGULATION | 2; static final int REWARD_NOTIFICATION_SEEN = TYPE_REWARD | 0; static final int REWARD_NOTIFICATION_INTERACTION = TYPE_REWARD | 1; Loading Loading @@ -347,8 +347,8 @@ public abstract class EconomicPolicy { return "BASIC_INCOME"; case REGULATION_BIRTHRIGHT: return "BIRTHRIGHT"; case REGULATION_TAX: return "TAX"; case REGULATION_WEALTH_RECLAMATION: return "WEALTH_RECLAMATION"; } return "UNKNOWN_REGULATION:" + Integer.toHexString(eventId); } Loading
apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java +55 −0 Original line number Diff line number Diff line Loading @@ -16,8 +16,12 @@ package com.android.server.tare; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlarmManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; Loading @@ -29,6 +33,7 @@ import android.os.BatteryManagerInternal; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.os.UserHandle; import android.util.ArraySet; import android.util.Log; Loading Loading @@ -57,6 +62,10 @@ public class InternalResourceService extends SystemService { public static final String TAG = "TARE-IRS"; public static final boolean DEBUG = Log.isLoggable("TARE", Log.DEBUG); static final long UNUSED_RECLAMATION_PERIOD_MS = 24 * HOUR_IN_MILLIS; /** How much of an app's unused wealth should be reclaimed periodically. */ private static final float DEFAULT_UNUSED_RECLAMATION_PERCENTAGE = .1f; /** Global local for all resource economy state. */ private final Object mLock = new Object(); Loading @@ -83,6 +92,9 @@ public class InternalResourceService extends SystemService { // In the range [0,100] to represent 0% to 100% battery. @GuardedBy("mLock") private int mCurrentBatteryLevel; // TODO: load from disk @GuardedBy("mLock") private long mLastUnusedReclamationTime; @SuppressWarnings("FieldCanBeLocal") private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { Loading Loading @@ -133,7 +145,21 @@ public class InternalResourceService extends SystemService { } }; private final AlarmManager.OnAlarmListener mUnusedWealthReclamationListener = new AlarmManager.OnAlarmListener() { @Override public void onAlarm() { synchronized (mLock) { mAgent.reclaimUnusedAssetsLocked(DEFAULT_UNUSED_RECLAMATION_PERCENTAGE); mLastUnusedReclamationTime = System.currentTimeMillis(); scheduleUnusedWealthReclamationLocked(); } } }; private static final int MSG_NOTIFY_BALANCE_CHANGE_LISTENERS = 0; private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1; private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*"; /** * Initializes the system service. Loading Loading @@ -186,6 +212,8 @@ public class InternalResourceService extends SystemService { } else { mIsSetup = true; } scheduleUnusedWealthReclamationLocked(); mCompleteEconomicPolicy.onSystemServicesReady(); } } } Loading Loading @@ -314,6 +342,26 @@ public class InternalResourceService extends SystemService { .sendToTarget(); } @GuardedBy("mLock") private void scheduleUnusedWealthReclamationLocked() { final long now = System.currentTimeMillis(); final long nextReclamationTime = Math.max(mLastUnusedReclamationTime + UNUSED_RECLAMATION_PERIOD_MS, now + 30_000); mHandler.post(() -> { // Never call out to AlarmManager with the lock held. This sits below AM. AlarmManager alarmManager = getContext().getSystemService(AlarmManager.class); if (alarmManager != null) { alarmManager.setWindow(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + (nextReclamationTime - now), 30 * MINUTE_IN_MILLIS, ALARM_TAG_WEALTH_RECLAMATION, mUnusedWealthReclamationListener, mHandler); } else { mHandler.sendEmptyMessageDelayed( MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT, 30_000); } }); } private int getCurrentBatteryLevel() { return mBatteryManagerInternal.getBatteryLevel(); } Loading Loading @@ -351,6 +399,13 @@ public class InternalResourceService extends SystemService { } } break; case MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT: removeMessages(MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT); synchronized (mLock) { scheduleUnusedWealthReclamationLocked(); } break; } } } Loading
apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java +11 −0 Original line number Diff line number Diff line Loading @@ -462,6 +462,17 @@ public class AppIdleHistory { return getElapsedTime(elapsedRealtime) - appUsageHistory.lastJobRunTime; } public long getTimeSinceLastUsedByUser(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName, elapsedRealtime, false); if (appUsageHistory == null || appUsageHistory.lastUsedByUserElapsedTime == Long.MIN_VALUE || appUsageHistory.lastUsedByUserElapsedTime == 0) { return Long.MAX_VALUE; } return getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedByUserElapsedTime; } public int getAppStandbyBucket(String packageName, int userId, long elapsedRealtime) { ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId); AppUsageHistory appUsageHistory = Loading