diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 31a5a050529bd..392df732953f2 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -490,8 +490,8 @@ public class AlarmManagerService extends SystemService { * holding the AlarmManagerService.mLock lock. */ @VisibleForTesting - final class Constants extends ContentObserver - implements DeviceConfig.OnPropertiesChangedListener { + final class Constants implements DeviceConfig.OnPropertiesChangedListener, + EconomyManagerInternal.TareStateChangeListener { @VisibleForTesting static final int MAX_EXACT_ALARM_DENY_LIST_SIZE = 250; @@ -695,7 +695,6 @@ public class AlarmManagerService extends SystemService { private int mVersion = 0; 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]; @@ -709,11 +708,12 @@ public class AlarmManagerService extends SystemService { } public void start() { - mInjector.registerContentObserver(this, - Settings.Global.getUriFor(Settings.Global.ENABLE_TARE)); mInjector.registerDeviceConfigListener(this); + final EconomyManagerInternal economyManagerInternal = + LocalServices.getService(EconomyManagerInternal.class); + economyManagerInternal.registerTareStateChangeListener(this); onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_ALARM_MANAGER)); - updateTareSettings(); + updateTareSettings(economyManagerInternal.isEnabled()); } public void updateAllowWhileIdleWhitelistDurationLocked() { @@ -886,15 +886,12 @@ public class AlarmManagerService extends SystemService { } @Override - public void onChange(boolean selfChange) { - updateTareSettings(); + public void onTareEnabledStateChanged(boolean isTareEnabled) { + updateTareSettings(isTareEnabled); } - private void updateTareSettings() { + private void updateTareSettings(boolean isTareEnabled) { 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 -> { diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 78140dce12f44..714c90b9b5a94 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -43,7 +43,6 @@ import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -54,7 +53,6 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ServiceInfo; -import android.database.ContentObserver; import android.net.Uri; import android.os.BatteryStats; import android.os.BatteryStatsInternal; @@ -118,6 +116,7 @@ import com.android.server.job.controllers.TimeController; import com.android.server.job.restrictions.JobRestriction; import com.android.server.job.restrictions.ThermalStatusRestriction; import com.android.server.pm.UserManagerInternal; +import com.android.server.tare.EconomyManagerInternal; import com.android.server.usage.AppStandbyInternal; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import com.android.server.utils.quota.Categorizer; @@ -356,40 +355,21 @@ public class JobSchedulerService extends com.android.server.SystemService // (ScheduledJobStateChanged and JobStatusDumpProto). public static final int RESTRICTED_INDEX = 5; - private class ConstantsObserver extends ContentObserver - implements DeviceConfig.OnPropertiesChangedListener { - private final ContentResolver mContentResolver; - - ConstantsObserver(Handler handler, Context context) { - super(handler); - mContentResolver = context.getContentResolver(); - } - + private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener, + EconomyManagerInternal.TareStateChangeListener { public void start() { DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_JOB_SCHEDULER, JobSchedulerBackgroundThread.getExecutor(), this); - mContentResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this); + final EconomyManagerInternal economyManagerInternal = + LocalServices.getService(EconomyManagerInternal.class); + economyManagerInternal.registerTareStateChangeListener(this); // Load all the constants. synchronized (mLock) { - mConstants.updateSettingsConstantsLocked(mContentResolver); + mConstants.updateTareSettingsLocked(economyManagerInternal.isEnabled()); } onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER)); } - @Override - public void onChange(boolean selfChange) { - synchronized (mLock) { - if (mConstants.updateSettingsConstantsLocked(mContentResolver)) { - for (int controller = 0; controller < mControllers.size(); controller++) { - final StateController sc = mControllers.get(controller); - sc.onConstantsUpdatedLocked(); - } - onControllerStateChanged(null); - } - } - } - @Override public void onPropertiesChanged(DeviceConfig.Properties properties) { boolean apiQuotaScheduleUpdated = false; @@ -465,6 +445,17 @@ public class JobSchedulerService extends com.android.server.SystemService } } } + + @Override + public void onTareEnabledStateChanged(boolean isTareEnabled) { + if (mConstants.updateTareSettingsLocked(isTareEnabled)) { + for (int controller = 0; controller < mControllers.size(); controller++) { + final StateController sc = mControllers.get(controller); + sc.onConstantsUpdatedLocked(); + } + onControllerStateChanged(null); + } + } } @VisibleForTesting @@ -719,10 +710,8 @@ public class JobSchedulerService extends com.android.server.SystemService DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS)); } - private boolean updateSettingsConstantsLocked(ContentResolver contentResolver) { + private boolean updateTareSettingsLocked(boolean isTareEnabled) { boolean changed = false; - final boolean isTareEnabled = Settings.Global.getInt(contentResolver, - Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1; if (USE_TARE_POLICY != isTareEnabled) { USE_TARE_POLICY = isTareEnabled; changed = true; @@ -1673,7 +1662,7 @@ public class JobSchedulerService extends com.android.server.SystemService mHandler = new JobHandler(context.getMainLooper()); mConstants = new Constants(); - mConstantsObserver = new ConstantsObserver(mHandler, context); + mConstantsObserver = new ConstantsObserver(); mJobSchedulerStub = new JobSchedulerStub(); mConcurrencyManager = new JobConcurrencyManager(this); diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java index 29aa94631d683..630f1e7bfb923 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomyManagerInternal.java @@ -119,6 +119,11 @@ public interface EconomyManagerInternal { boolean canAfford); } + /** Listener for various TARE state changes. */ + interface TareStateChangeListener { + void onTareEnabledStateChanged(boolean isTareEnabled); + } + /** * Return {@code true} if the app is able to pay for the anticipated actions. */ @@ -130,6 +135,9 @@ public interface EconomyManagerInternal { */ long getMaxDurationMs(int userId, @NonNull String pkgName, @NonNull ActionBill bill); + /** Returns true if TARE is enabled. */ + boolean isEnabled(); + /** * Register an {@link AffordabilityChangeListener} to track when an app's ability to afford the * indicated bill changes. @@ -144,6 +152,16 @@ public interface EconomyManagerInternal { void unregisterAffordabilityChangeListener(int userId, @NonNull String pkgName, @NonNull AffordabilityChangeListener listener, @NonNull ActionBill bill); + /** + * Register a {@link TareStateChangeListener} to track when TARE's state changes. + */ + void registerTareStateChangeListener(@NonNull TareStateChangeListener listener); + + /** + * Unregister a {@link TareStateChangeListener} from being notified when TARE's state changes. + */ + void unregisterTareStateChangeListener(@NonNull TareStateChangeListener listener); + /** * Note that an instantaneous event has occurred. The event must be specified in one of the * EconomicPolicies. 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 ed11097202e19..437a10198d8e4 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java @@ -53,6 +53,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; +import android.provider.DeviceConfig; import android.provider.Settings; import android.util.ArraySet; import android.util.IndentingPrintWriter; @@ -68,11 +69,13 @@ import com.android.internal.util.DumpUtils; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.pm.UserManagerInternal; +import com.android.server.tare.EconomyManagerInternal.TareStateChangeListener; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CopyOnWriteArraySet; /** * Responsible for handling app's ARC count based on events, ensuring ARCs are credited when @@ -169,6 +172,9 @@ public class InternalResourceService extends SystemService { @GuardedBy("mPackageToUidCache") private final SparseArrayMap mPackageToUidCache = new SparseArrayMap<>(); + private final CopyOnWriteArraySet mStateChangeListeners = + new CopyOnWriteArraySet<>(); + /** List of packages that are "exempted" from battery restrictions. */ // TODO(144864180): include userID @GuardedBy("mLock") @@ -261,6 +267,7 @@ public class InternalResourceService extends SystemService { private static final int MSG_SCHEDULE_UNUSED_WEALTH_RECLAMATION_EVENT = 1; private static final int MSG_PROCESS_USAGE_EVENT = 2; private static final int MSG_MAYBE_FORCE_RECLAIM = 3; + private static final int MSG_NOTIFY_STATE_CHANGE_LISTENERS = 4; private static final String ALARM_TAG_WEALTH_RECLAMATION = "*tare.reclamation*"; /** @@ -802,6 +809,13 @@ public class InternalResourceService extends SystemService { } break; + case MSG_NOTIFY_STATE_CHANGE_LISTENERS: { + for (TareStateChangeListener listener : mStateChangeListeners) { + listener.onTareEnabledStateChanged(mIsEnabled); + } + } + break; + case MSG_PROCESS_USAGE_EVENT: { final int userId = msg.arg1; final UsageEvents.Event event = (UsageEvents.Event) msg.obj; @@ -891,6 +905,16 @@ public class InternalResourceService extends SystemService { } } + @Override + public void registerTareStateChangeListener(@NonNull TareStateChangeListener listener) { + mStateChangeListeners.add(listener); + } + + @Override + public void unregisterTareStateChangeListener(@NonNull TareStateChangeListener listener) { + mStateChangeListeners.remove(listener); + } + @Override public boolean canPayFor(int userId, @NonNull String pkgName, @NonNull ActionBill bill) { if (!mIsEnabled) { @@ -943,6 +967,11 @@ public class InternalResourceService extends SystemService { } } + @Override + public boolean isEnabled() { + return mIsEnabled; + } + @Override public void noteInstantaneousEvent(int userId, @NonNull String pkgName, int eventId, @Nullable String tag) { @@ -980,7 +1009,10 @@ public class InternalResourceService extends SystemService { } } - private class ConfigObserver extends ContentObserver { + private class ConfigObserver extends ContentObserver + implements DeviceConfig.OnPropertiesChangedListener { + private static final String KEY_DC_ENABLE_TARE = "enable_tare"; + private final ContentResolver mContentResolver; ConfigObserver(Handler handler, Context context) { @@ -989,12 +1021,15 @@ public class InternalResourceService extends SystemService { } public void start() { + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_TARE, + TareHandlerThread.getExecutor(), this); mContentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this); mContentResolver.registerContentObserver( Settings.Global.getUriFor(TARE_ALARM_MANAGER_CONSTANTS), false, this); mContentResolver.registerContentObserver( Settings.Global.getUriFor(TARE_JOB_SCHEDULER_CONSTANTS), false, this); + onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_TARE)); updateEnabledStatus(); } @@ -1008,9 +1043,31 @@ public class InternalResourceService extends SystemService { } } + @Override + public void onPropertiesChanged(DeviceConfig.Properties properties) { + synchronized (mLock) { + for (String name : properties.getKeyset()) { + if (name == null) { + continue; + } + switch (name) { + case KEY_DC_ENABLE_TARE: + updateEnabledStatus(); + break; + } + } + } + } + private void updateEnabledStatus() { + // User setting should override DeviceConfig setting. + // NOTE: There's currently no way for a user to reset the value (via UI), so if a user + // manually toggles TARE via UI, we'll always defer to the user's current setting + // TODO: add a "reset" value if the user toggle is an issue + final boolean isTareEnabledDC = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_TARE, + KEY_DC_ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE == 1); final boolean isTareEnabled = Settings.Global.getInt(mContentResolver, - Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1; + Settings.Global.ENABLE_TARE, isTareEnabledDC ? 1 : 0) == 1; if (mIsEnabled != isTareEnabled) { mIsEnabled = isTareEnabled; if (mIsEnabled) { @@ -1018,6 +1075,7 @@ public class InternalResourceService extends SystemService { } else { tearDownEverything(); } + mHandler.sendEmptyMessage(MSG_NOTIFY_STATE_CHANGE_LISTENERS); } } diff --git a/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java index 5b2b6607ba143..65ef8bfe0d06c 100644 --- a/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java +++ b/apex/jobscheduler/service/java/com/android/server/tare/TareHandlerThread.java @@ -17,10 +17,13 @@ package com.android.server.tare; import android.os.Handler; +import android.os.HandlerExecutor; import android.os.HandlerThread; import android.os.Looper; import android.os.Trace; +import java.util.concurrent.Executor; + /** * Singleton thread for all of TARE. * @@ -29,6 +32,7 @@ import android.os.Trace; final class TareHandlerThread extends HandlerThread { private static TareHandlerThread sInstance; + private static Executor sHandlerExecutor; private static Handler sHandler; private TareHandlerThread() { @@ -42,6 +46,7 @@ final class TareHandlerThread extends HandlerThread { final Looper looper = sInstance.getLooper(); looper.setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER); sHandler = new Handler(sInstance.getLooper()); + sHandlerExecutor = new HandlerExecutor(sHandler); } } @@ -52,6 +57,14 @@ final class TareHandlerThread extends HandlerThread { return sInstance; } + /** Returns the singleton handler executor for TareHandlerThread */ + public static Executor getExecutor() { + synchronized (TareHandlerThread.class) { + ensureThreadLocked(); + return sHandlerExecutor; + } + } + /** Returns the singleton handler for TareHandlerThread. */ public static Handler getHandler() { synchronized (TareHandlerThread.class) { diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index 61cd4448cc353..a9099ae35b75c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -621,12 +621,8 @@ public class AlarmManagerServiceTest { } 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); + when(mEconomyManagerInternal.isEnabled()).thenReturn(enabled); + mService.mConstants.onTareEnabledStateChanged(enabled); } /**