From 739dd166c181a3137cbef26dc44b3715f4291738 Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Tue, 20 Jul 2021 12:07:12 -0700 Subject: [PATCH] Add TARE enable constant. Add a Settings constant to control the overall enabling of TARE. If disabled, it will clean up and drop as much as possible. Set the default value to 0/false. Bug: 158300259 Test: look at dumpsys output after changing setting constant value Test: atest SettingsProviderTest:SettingsBackupTest Change-Id: I252f792444423fe5dc801125f9e7d3a0b6ed5b42 --- .../java/com/android/server/tare/Agent.java | 31 +++- .../android/server/tare/ChargingModifier.java | 15 +- .../server/tare/DeviceIdleModifier.java | 15 +- .../android/server/tare/EconomicPolicy.java | 14 +- .../server/tare/InternalResourceService.java | 148 ++++++++++++++---- .../com/android/server/tare/Modifier.java | 5 +- .../server/tare/ProcessStateModifier.java | 14 +- core/java/android/provider/Settings.java | 15 ++ .../validators/GlobalSettingsValidators.java | 1 + .../android/provider/SettingsBackupTest.java | 1 + 10 files changed, 221 insertions(+), 38 deletions(-) diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java index 7f09e712fa6a1..74ed334df8970 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java @@ -196,7 +196,6 @@ class Agent { final int eventId, @Nullable String tag) { final long now = System.currentTimeMillis(); final Ledger ledger = getLedgerLocked(userId, pkgName); - final boolean wasSolvent = getBalanceLocked(userId, pkgName) > 0; final int eventType = getEventType(eventId); switch (eventType) { @@ -400,12 +399,16 @@ class Agent { SparseArrayMap ongoingEvents = mCurrentOngoingEvents.get(userId, pkgName); if (ongoingEvents == null) { - Slog.wtf(TAG, "No ongoing transactions :/"); + // This may occur if TARE goes from disabled to enabled while an event is already + // occurring. + Slog.w(TAG, "No ongoing transactions for <" + userId + ">" + pkgName); return; } final OngoingEvent ongoingEvent = ongoingEvents.get(eventId, tag); if (ongoingEvent == null) { - Slog.wtf(TAG, "Nonexistent ongoing transaction " + // This may occur if TARE goes from disabled to enabled while an event is already + // occurring. + Slog.w(TAG, "Nonexistent ongoing transaction " + eventToString(eventId) + (tag == null ? "" : ":" + tag) + " for <" + userId + ">" + pkgName + " ended"); return; @@ -768,6 +771,15 @@ class Agent { SystemClock.elapsedRealtime() + timeToThresholdMs); } + @GuardedBy("mLock") + void tearDownLocked() { + mLedgers.clear(); + mCurrentNarcsInCirculation = 0; + mCurrentOngoingEvents.clear(); + mBalanceThresholdAlarmListener.dropAllAlarmsLocked(); + mLedgerCleanupAlarmListener.dropAllAlarmsLocked(); + } + @VisibleForTesting static class OngoingEvent { public final long startTimeElapsed; @@ -986,6 +998,12 @@ class Agent { } } + @GuardedBy("mLock") + void dropAllAlarmsLocked() { + mAlarmQueue.clear(); + setNextAlarmLocked(0); + } + @GuardedBy("mLock") protected abstract void processExpiredAlarmLocked(int userId, @NonNull String packageName); @@ -1069,6 +1087,13 @@ class Agent { final ActionAffordabilityNote note = new ActionAffordabilityNote(bill, listener, mCompleteEconomicPolicy); if (actionAffordabilityNotes.add(note)) { + if (!mIrs.isEnabled()) { + // When TARE isn't enabled, we always say something is affordable. We also don't + // want to silently drop affordability change listeners in case TARE becomes enabled + // because then clients will be in an ambiguous state. + note.setNewAffordability(true); + return; + } note.recalculateModifiedPrice(mCompleteEconomicPolicy, userId, pkgName); note.setNewAffordability( getBalanceLocked(userId, pkgName) >= note.getCachedModifiedPrice()); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java index a627de691dab0..712e13eb14e24 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/ChargingModifier.java @@ -40,7 +40,16 @@ class ChargingModifier extends Modifier { super(); mIrs = irs; mChargingTracker = new ChargingTracker(); - mChargingTracker.startTracking(irs.getContext()); + } + + @Override + public void setup() { + mChargingTracker.startTracking(mIrs.getContext()); + } + + @Override + public void tearDown() { + mChargingTracker.stopTracking(mIrs.getContext()); } @Override @@ -84,6 +93,10 @@ class ChargingModifier extends Modifier { mCharging = batteryManager.isCharging(); } + public void stopTracking(@NonNull Context context) { + context.unregisterReceiver(this); + } + @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java index 20b9c70d9b97c..37b0aa8f48622 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/DeviceIdleModifier.java @@ -40,7 +40,16 @@ class DeviceIdleModifier extends Modifier { mIrs = irs; mPowerManager = irs.getContext().getSystemService(PowerManager.class); mDeviceIdleTracker = new DeviceIdleTracker(); - mDeviceIdleTracker.startTracking(irs.getContext()); + } + + @Override + public void setup() { + mDeviceIdleTracker.startTracking(mIrs.getContext()); + } + + @Override + public void tearDown() { + mDeviceIdleTracker.stopTracking(mIrs.getContext()); } @Override @@ -81,6 +90,10 @@ class DeviceIdleModifier extends Modifier { mDeviceLightIdle = mPowerManager.isLightDeviceIdleMode(); } + void stopTracking(@NonNull Context context) { + context.unregisterReceiver(this); + } + @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java index f08dd24b38415..fa14d107bd552 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java @@ -157,11 +157,21 @@ public abstract class EconomicPolicy { } @CallSuper - void onSystemServicesReady() { + void setup() { for (int i = 0; i < NUM_COST_MODIFIERS; ++i) { final Modifier modifier = COST_MODIFIER_BY_INDEX[i]; if (modifier != null) { - modifier.onSystemServicesReady(); + modifier.setup(); + } + } + } + + @CallSuper + void tearDown() { + for (int i = 0; i < NUM_COST_MODIFIERS; ++i) { + final Modifier modifier = COST_MODIFIER_BY_INDEX[i]; + if (modifier != null) { + modifier.tearDown(); } } } diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java index 0bbb8029b380f..67d7a95edbb8b 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java @@ -24,11 +24,13 @@ import android.annotation.Nullable; import android.app.AlarmManager; import android.app.tare.IEconomyManager; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.database.ContentObserver; import android.net.Uri; import android.os.BatteryManagerInternal; import android.os.Binder; @@ -38,6 +40,7 @@ import android.os.Looper; import android.os.Message; import android.os.SystemClock; import android.os.UserHandle; +import android.provider.Settings; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Log; @@ -81,6 +84,7 @@ public class InternalResourceService extends SystemService { private final Agent mAgent; private final CompleteEconomicPolicy mCompleteEconomicPolicy; + private final ConfigObserver mConfigObserver; private final EconomyManagerStub mEconomyManagerStub; @NonNull @@ -91,8 +95,8 @@ public class InternalResourceService extends SystemService { @GuardedBy("mLock") private final SparseSetArray mUidToPackageCache = new SparseSetArray<>(); - @GuardedBy("mLock") - private boolean mIsSetup; + private volatile boolean mIsEnabled; + private volatile int mBootPhase; // In the range [0,100] to represent 0% to 100% battery. @GuardedBy("mLock") private int mCurrentBatteryLevel; @@ -185,18 +189,7 @@ public class InternalResourceService extends SystemService { mCompleteEconomicPolicy = new CompleteEconomicPolicy(this); mAgent = new Agent(this, mCompleteEconomicPolicy); - final IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED); - context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); - final IntentFilter pkgFilter = new IntentFilter(); - pkgFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); - pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); - pkgFilter.addDataScheme("package"); - context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, pkgFilter, null, null); - final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); - userFilter.addAction(Intent.ACTION_USER_ADDED); - context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); + mConfigObserver = new ConfigObserver(mHandler, context); publishLocalService(EconomyManagerInternal.class, new LocalService()); } @@ -208,19 +201,11 @@ public class InternalResourceService extends SystemService { @Override public void onBootPhase(int phase) { + mBootPhase = phase; + if (PHASE_SYSTEM_SERVICES_READY == phase) { - synchronized (mLock) { - mCurrentBatteryLevel = getCurrentBatteryLevel(); - // TODO: base on if we have anything persisted - final boolean isFirstSetup = true; - if (isFirstSetup) { - mHandler.post(this::setupEconomy); - } else { - mIsSetup = true; - } - scheduleUnusedWealthReclamationLocked(); - mCompleteEconomicPolicy.onSystemServicesReady(); - } + mConfigObserver.start(); + setupEverything(); } } @@ -247,6 +232,10 @@ public class InternalResourceService extends SystemService { / 100; } + boolean isEnabled() { + return mIsEnabled; + } + void onBatteryLevelChanged() { synchronized (mLock) { final int newBatteryLevel = getCurrentBatteryLevel(); @@ -396,11 +385,67 @@ public class InternalResourceService extends SystemService { mPkgCache = mPackageManager.getInstalledPackages(0); } - private void setupEconomy() { + private void registerReceivers() { + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_BATTERY_LEVEL_CHANGED); + getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); + + final IntentFilter pkgFilter = new IntentFilter(); + pkgFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); + pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); + pkgFilter.addDataScheme("package"); + getContext() + .registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, pkgFilter, null, null); + + final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED); + userFilter.addAction(Intent.ACTION_USER_ADDED); + getContext() + .registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); + } + + /** Perform long-running and/or heavy setup work. This should be called off the main thread. */ + private void setupHeavyWork() { synchronized (mLock) { loadInstalledPackageListLocked(); - mAgent.grantBirthrightsLocked(); - mIsSetup = true; + // TODO: base on if we have anything persisted + final boolean isFirstSetup = true; + if (isFirstSetup) { + mAgent.grantBirthrightsLocked(); + } + } + } + + private void setupEverything() { + if (mBootPhase < PHASE_SYSTEM_SERVICES_READY || !mIsEnabled) { + return; + } + synchronized (mLock) { + registerReceivers(); + mCurrentBatteryLevel = getCurrentBatteryLevel(); + mHandler.post(this::setupHeavyWork); + scheduleUnusedWealthReclamationLocked(); + mCompleteEconomicPolicy.setup(); + } + } + + private void tearDownEverything() { + if (mIsEnabled) { + return; + } + synchronized (mLock) { + mAgent.tearDownLocked(); + mCompleteEconomicPolicy.tearDown(); + 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.cancel(mUnusedWealthReclamationListener); + } + }); + mPkgCache.clear(); + mUidToPackageCache.clear(); + getContext().unregisterReceiver(mBroadcastReceiver); } } @@ -487,6 +532,9 @@ public class InternalResourceService extends SystemService { @Override public boolean canPayFor(int userId, @NonNull String pkgName, @NonNull ActionBill bill) { + if (!mIsEnabled) { + return true; + } // TODO: take temp-allowlist into consideration long requiredBalance = 0; final List projectedActions = @@ -506,6 +554,9 @@ public class InternalResourceService extends SystemService { @Override public void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId, @Nullable String tag) { + if (!mIsEnabled) { + return; + } synchronized (mLock) { mAgent.noteInstantaneousEventLocked(userId, pkgName, eventId, tag); } @@ -514,6 +565,9 @@ public class InternalResourceService extends SystemService { @Override public void noteOngoingEventStarted(int userId, @NonNull String pkgName, int eventId, @Nullable String tag) { + if (!mIsEnabled) { + return; + } synchronized (mLock) { final long nowElapsed = SystemClock.elapsedRealtime(); mAgent.noteOngoingEventLocked(userId, pkgName, eventId, tag, nowElapsed); @@ -523,6 +577,9 @@ public class InternalResourceService extends SystemService { @Override public void noteOngoingEventStopped(int userId, @NonNull String pkgName, int eventId, @Nullable String tag) { + if (!mIsEnabled) { + return; + } final long nowElapsed = SystemClock.elapsedRealtime(); final long now = System.currentTimeMillis(); synchronized (mLock) { @@ -531,6 +588,39 @@ public class InternalResourceService extends SystemService { } } + private class ConfigObserver extends ContentObserver { + private final ContentResolver mContentResolver; + + ConfigObserver(Handler handler, Context context) { + super(handler); + mContentResolver = context.getContentResolver(); + } + + public void start() { + mContentResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this); + updateConfig(); + } + + @Override + public void onChange(boolean selfChange) { + updateConfig(); + } + + private void updateConfig() { + final boolean isTareEnabled = Settings.Global.getInt(mContentResolver, + Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1; + if (mIsEnabled != isTareEnabled) { + mIsEnabled = isTareEnabled; + if (mIsEnabled) { + setupEverything(); + } else { + tearDownEverything(); + } + } + } + } + private static void dumpHelp(PrintWriter pw) { pw.println("Resource Economy (economy) dump options:"); pw.println(" [-h|--help] [package] ..."); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java b/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java index 6b89b796fae85..311b6cb7910fc 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/Modifier.java @@ -60,7 +60,10 @@ abstract class Modifier { return price; } - void onSystemServicesReady() { + void setup() { + } + + void tearDown() { } abstract void dump(IndentingPrintWriter pw); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java index 2ee645990df54..92a2014a66006 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java @@ -110,7 +110,7 @@ class ProcessStateModifier extends Modifier { @Override @GuardedBy("mLock") - void onSystemServicesReady() { + void setup() { try { ActivityManager.getService().registerUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE, @@ -120,6 +120,18 @@ class ProcessStateModifier extends Modifier { } } + @Override + @GuardedBy("mLock") + void tearDown() { + try { + ActivityManager.getService().unregisterUidObserver(mUidObserver); + } catch (RemoteException e) { + // ignored; both services live in system_server + } + mPackageToUidCache.clear(); + mUidProcStateBucketCache.clear(); + } + /** * Get the final modified price based on an app's process state. * diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d824226a3270e..a0adb67f85731 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -13433,6 +13433,21 @@ public final class Settings { public static final String FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED = "forced_app_standby_for_small_battery_enabled"; + /** + * Whether to enable the TARE subsystem as a whole or not. + * 1 means enable, 0 means disable. + * + * @hide + */ + public static final String ENABLE_TARE = "enable_tare"; + + /** + * Default value for {@link #ENABLE_TARE}. + * + * @hide + */ + public static final int DEFAULT_ENABLE_TARE = 0; + /** * Whether or not to enable the User Absent, Radios Off feature on small battery devices. * Type: int (0 for false, 1 for true) diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java index f0c2e0de16297..ecfef6207c0e1 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java @@ -116,6 +116,7 @@ public class GlobalSettingsValidators { VALIDATORS.put(Global.CLOCKWORK_SYSUI_MAIN_ACTIVITY_NAME, ANY_STRING_VALIDATOR); VALIDATORS.put(Global.CLOCKWORK_SYSUI_PACKAGE_NAME, ANY_STRING_VALIDATOR); VALIDATORS.put(Global.CLOCKWORK_HOME_READY, ANY_STRING_VALIDATOR); + VALIDATORS.put(Global.ENABLE_TARE, BOOLEAN_VALIDATOR); VALIDATORS.put(Global.PRIVATE_DNS_MODE, ANY_STRING_VALIDATOR); VALIDATORS.put(Global.PRIVATE_DNS_SPECIFIER, ANY_STRING_VALIDATOR); VALIDATORS.put(Global.SOFT_AP_TIMEOUT_ENABLED, BOOLEAN_VALIDATOR); diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 9e3691f479cc0..07b868214d908 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -265,6 +265,7 @@ public class SettingsBackupTest { Settings.Global.ENABLE_DISKSTATS_LOGGING, Settings.Global.ENABLE_EPHEMERAL_FEATURE, Settings.Global.ENABLE_RESTRICTED_BUCKET, + Settings.Global.ENABLE_TARE, Settings.Global.DYNAMIC_POWER_SAVINGS_ENABLED, Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,