Loading apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java +183 −55 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import android.util.SparseArray; import android.util.SparseArrayMap; import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.SparseSetArray; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -158,19 +159,6 @@ public final class FlexibilityController extends StateController { @GuardedBy("mLock") private final SparseLongArray mLastSeenConstraintTimesElapsed = new SparseLongArray(); private DeviceIdleInternal mDeviceIdleInternal; private final ArraySet<String> mPowerAllowlistedApps = new ArraySet<>(); private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { switch (intent.getAction()) { case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED: mHandler.post(FlexibilityController.this::updatePowerAllowlistCache); break; } } }; @VisibleForTesting @GuardedBy("mLock") final FlexibilityTracker mFlexibilityTracker; Loading @@ -182,6 +170,7 @@ public final class FlexibilityController extends StateController { private final FcHandler mHandler; @VisibleForTesting final PrefetchController mPrefetchController; private final SpecialAppTracker mSpecialAppTracker; /** * Stores the beginning of prefetch jobs lifecycle per app as a maximum of Loading Loading @@ -355,16 +344,16 @@ public final class FlexibilityController extends StateController { mPercentsToDropConstraints = FcConfig.DEFAULT_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS; mPrefetchController = prefetchController; mSpecialAppTracker = new SpecialAppTracker(); if (mFlexibilityEnabled) { registerBroadcastReceiver(); mSpecialAppTracker.startTracking(); } } @Override public void onSystemServicesReady() { mDeviceIdleInternal = LocalServices.getService(DeviceIdleInternal.class); mHandler.post(FlexibilityController.this::updatePowerAllowlistCache); mSpecialAppTracker.onSystemServicesReady(); } @Override Loading Loading @@ -453,6 +442,7 @@ public final class FlexibilityController extends StateController { final int userId = UserHandle.getUserId(uid); mPrefetchLifeCycleStart.delete(userId, packageName); mJobScoreTrackers.delete(uid, packageName); mSpecialAppTracker.onAppRemoved(userId, packageName); for (int i = mJobsToCheck.size() - 1; i >= 0; --i) { final JobStatus js = mJobsToCheck.valueAt(i); if ((js.getSourceUid() == uid && js.getSourcePackageName().equals(packageName)) Loading @@ -466,6 +456,7 @@ public final class FlexibilityController extends StateController { @GuardedBy("mLock") public void onUserRemovedLocked(int userId) { mPrefetchLifeCycleStart.delete(userId); mSpecialAppTracker.onUserRemoved(userId); for (int u = mJobScoreTrackers.numMaps() - 1; u >= 0; --u) { final int uid = mJobScoreTrackers.keyAt(u); if (UserHandle.getUserId(uid) == userId) { Loading Loading @@ -496,9 +487,10 @@ public final class FlexibilityController extends StateController { // Only exclude DEFAULT+ priority jobs for BFGS+ apps || (mService.getUidBias(js.getSourceUid()) >= JobInfo.BIAS_BOUND_FOREGROUND_SERVICE && js.getEffectivePriority() >= PRIORITY_DEFAULT) // For apps in the power allowlist, automatically exclude DEFAULT+ priority jobs. // For special/privileged apps, automatically exclude DEFAULT+ priority jobs. || (js.getEffectivePriority() >= PRIORITY_DEFAULT && mPowerAllowlistedApps.contains(js.getSourcePackageName())) && mSpecialAppTracker.isSpecialApp( js.getSourceUserId(), js.getSourcePackageName())) || hasEnoughSatisfiedConstraintsLocked(js) || mService.isCurrentlyRunningLocked(js); } Loading Loading @@ -827,39 +819,6 @@ public final class FlexibilityController extends StateController { mFcConfig.processConstantLocked(properties, key); } private void registerBroadcastReceiver() { IntentFilter filter = new IntentFilter(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED); mContext.registerReceiver(mBroadcastReceiver, filter); } private void unregisterBroadcastReceiver() { mContext.unregisterReceiver(mBroadcastReceiver); } private void updatePowerAllowlistCache() { if (mDeviceIdleInternal == null) { return; } // Don't call out to DeviceIdleController with the lock held. final String[] allowlistedPkgs = mDeviceIdleInternal.getFullPowerWhitelistExceptIdle(); final ArraySet<String> changedPkgs = new ArraySet<>(); synchronized (mLock) { changedPkgs.addAll(mPowerAllowlistedApps); mPowerAllowlistedApps.clear(); for (final String pkgName : allowlistedPkgs) { mPowerAllowlistedApps.add(pkgName); if (changedPkgs.contains(pkgName)) { changedPkgs.remove(pkgName); } else { changedPkgs.add(pkgName); } } mPackagesToCheck.addAll(changedPkgs); mHandler.sendEmptyMessage(MSG_CHECK_PACKAGES); } } @VisibleForTesting class FlexibilityTracker { final ArrayList<ArraySet<JobStatus>> mTrackedJobs; Loading Loading @@ -1343,12 +1302,12 @@ public final class FlexibilityController extends StateController { mFlexibilityEnabled = true; mPrefetchController .registerPrefetchChangedListener(mPrefetchChangedListener); registerBroadcastReceiver(); mSpecialAppTracker.startTracking(); } else { mFlexibilityEnabled = false; mPrefetchController .unRegisterPrefetchChangedListener(mPrefetchChangedListener); unregisterBroadcastReceiver(); mSpecialAppTracker.stopTracking(); } } break; Loading Loading @@ -1653,6 +1612,176 @@ public final class FlexibilityController extends StateController { return mFcConfig; } private class SpecialAppTracker { /** * Lock for objects inside this class. This should never be held when attempting to acquire * {@link #mLock}. It is fine to acquire this if already holding {@link #mLock}. */ private final Object mSatLock = new Object(); private DeviceIdleInternal mDeviceIdleInternal; /** Set of all apps that have been deemed special, keyed by user ID. */ private final SparseSetArray<String> mSpecialApps = new SparseSetArray<>(); @GuardedBy("mSatLock") private final ArraySet<String> mPowerAllowlistedApps = new ArraySet<>(); private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { switch (intent.getAction()) { case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED: mHandler.post(SpecialAppTracker.this::updatePowerAllowlistCache); break; } } }; public boolean isSpecialApp(final int userId, @NonNull String packageName) { synchronized (mSatLock) { if (mSpecialApps.contains(UserHandle.USER_ALL, packageName)) { return true; } if (mSpecialApps.contains(userId, packageName)) { return true; } } return false; } private boolean isSpecialAppInternal(final int userId, @NonNull String packageName) { synchronized (mSatLock) { if (mPowerAllowlistedApps.contains(packageName)) { return true; } } return false; } private void onAppRemoved(final int userId, String packageName) { synchronized (mSatLock) { // Don't touch the USER_ALL set here. If the app is completely removed from the // device, any list that affects USER_ALL should update and this would eventually // be updated with those lists no longer containing the app. mSpecialApps.remove(userId, packageName); } } private void onSystemServicesReady() { mDeviceIdleInternal = LocalServices.getService(DeviceIdleInternal.class); synchronized (mLock) { if (mFlexibilityEnabled) { mHandler.post(SpecialAppTracker.this::updatePowerAllowlistCache); } } } private void onUserRemoved(final int userId) { synchronized (mSatLock) { mSpecialApps.remove(userId); } } private void startTracking() { IntentFilter filter = new IntentFilter( PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED); mContext.registerReceiver(mBroadcastReceiver, filter); updatePowerAllowlistCache(); } private void stopTracking() { mContext.unregisterReceiver(mBroadcastReceiver); synchronized (mSatLock) { mPowerAllowlistedApps.clear(); mSpecialApps.clear(); } } /** * Update the processed special app set for the specified user ID, only looking at the * specified set of apps. This method must <b>NEVER</b> be called while holding * {@link #mSatLock}. */ private void updateSpecialAppSetUnlocked(final int userId, @NonNull ArraySet<String> pkgs) { // This method may need to acquire mLock, so ensure that mSatLock isn't held to avoid // lock inversion. if (Thread.holdsLock(mSatLock)) { throw new IllegalStateException("Must never hold local mSatLock"); } if (pkgs.size() == 0) { return; } final ArraySet<String> changedPkgs = new ArraySet<>(); synchronized (mSatLock) { for (int i = pkgs.size() - 1; i >= 0; --i) { final String pkgName = pkgs.valueAt(i); if (isSpecialAppInternal(userId, pkgName)) { if (mSpecialApps.add(userId, pkgName)) { changedPkgs.add(pkgName); } } else if (mSpecialApps.remove(userId, pkgName)) { changedPkgs.add(pkgName); } } } if (changedPkgs.size() > 0) { synchronized (mLock) { mPackagesToCheck.addAll(changedPkgs); mHandler.sendEmptyMessage(MSG_CHECK_PACKAGES); } } } private void updatePowerAllowlistCache() { if (mDeviceIdleInternal == null) { return; } // Don't call out to DeviceIdleController with the lock held. final String[] allowlistedPkgs = mDeviceIdleInternal.getFullPowerWhitelistExceptIdle(); final ArraySet<String> changedPkgs = new ArraySet<>(); synchronized (mSatLock) { changedPkgs.addAll(mPowerAllowlistedApps); mPowerAllowlistedApps.clear(); for (String pkgName : allowlistedPkgs) { mPowerAllowlistedApps.add(pkgName); if (!changedPkgs.remove(pkgName)) { // The package wasn't in the previous set of allowlisted apps. Add it // since its state has changed. changedPkgs.add(pkgName); } } } // The full allowlist is currently user-agnostic, so use USER_ALL for these packages. updateSpecialAppSetUnlocked(UserHandle.USER_ALL, changedPkgs); } public void dump(@NonNull IndentingPrintWriter pw) { pw.println("Special apps:"); pw.increaseIndent(); synchronized (mSatLock) { for (int u = 0; u < mSpecialApps.size(); ++u) { pw.print(mSpecialApps.keyAt(u)); pw.print(": "); pw.println(mSpecialApps.valuesAt(u)); } pw.println(); pw.print("Power allowlisted packages: "); pw.println(mPowerAllowlistedApps); } pw.decreaseIndent(); } } @Override @GuardedBy("mLock") public void dumpConstants(IndentingPrintWriter pw) { Loading Loading @@ -1690,8 +1819,7 @@ public final class FlexibilityController extends StateController { pw.decreaseIndent(); pw.println(); pw.print("Power allowlisted packages: "); pw.println(mPowerAllowlistedApps); mSpecialAppTracker.dump(pw); pw.println(); mFlexibilityTracker.dump(pw, predicate, nowElapsed); Loading Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java +183 −55 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import android.util.SparseArray; import android.util.SparseArrayMap; import android.util.SparseIntArray; import android.util.SparseLongArray; import android.util.SparseSetArray; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -158,19 +159,6 @@ public final class FlexibilityController extends StateController { @GuardedBy("mLock") private final SparseLongArray mLastSeenConstraintTimesElapsed = new SparseLongArray(); private DeviceIdleInternal mDeviceIdleInternal; private final ArraySet<String> mPowerAllowlistedApps = new ArraySet<>(); private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { switch (intent.getAction()) { case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED: mHandler.post(FlexibilityController.this::updatePowerAllowlistCache); break; } } }; @VisibleForTesting @GuardedBy("mLock") final FlexibilityTracker mFlexibilityTracker; Loading @@ -182,6 +170,7 @@ public final class FlexibilityController extends StateController { private final FcHandler mHandler; @VisibleForTesting final PrefetchController mPrefetchController; private final SpecialAppTracker mSpecialAppTracker; /** * Stores the beginning of prefetch jobs lifecycle per app as a maximum of Loading Loading @@ -355,16 +344,16 @@ public final class FlexibilityController extends StateController { mPercentsToDropConstraints = FcConfig.DEFAULT_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS; mPrefetchController = prefetchController; mSpecialAppTracker = new SpecialAppTracker(); if (mFlexibilityEnabled) { registerBroadcastReceiver(); mSpecialAppTracker.startTracking(); } } @Override public void onSystemServicesReady() { mDeviceIdleInternal = LocalServices.getService(DeviceIdleInternal.class); mHandler.post(FlexibilityController.this::updatePowerAllowlistCache); mSpecialAppTracker.onSystemServicesReady(); } @Override Loading Loading @@ -453,6 +442,7 @@ public final class FlexibilityController extends StateController { final int userId = UserHandle.getUserId(uid); mPrefetchLifeCycleStart.delete(userId, packageName); mJobScoreTrackers.delete(uid, packageName); mSpecialAppTracker.onAppRemoved(userId, packageName); for (int i = mJobsToCheck.size() - 1; i >= 0; --i) { final JobStatus js = mJobsToCheck.valueAt(i); if ((js.getSourceUid() == uid && js.getSourcePackageName().equals(packageName)) Loading @@ -466,6 +456,7 @@ public final class FlexibilityController extends StateController { @GuardedBy("mLock") public void onUserRemovedLocked(int userId) { mPrefetchLifeCycleStart.delete(userId); mSpecialAppTracker.onUserRemoved(userId); for (int u = mJobScoreTrackers.numMaps() - 1; u >= 0; --u) { final int uid = mJobScoreTrackers.keyAt(u); if (UserHandle.getUserId(uid) == userId) { Loading Loading @@ -496,9 +487,10 @@ public final class FlexibilityController extends StateController { // Only exclude DEFAULT+ priority jobs for BFGS+ apps || (mService.getUidBias(js.getSourceUid()) >= JobInfo.BIAS_BOUND_FOREGROUND_SERVICE && js.getEffectivePriority() >= PRIORITY_DEFAULT) // For apps in the power allowlist, automatically exclude DEFAULT+ priority jobs. // For special/privileged apps, automatically exclude DEFAULT+ priority jobs. || (js.getEffectivePriority() >= PRIORITY_DEFAULT && mPowerAllowlistedApps.contains(js.getSourcePackageName())) && mSpecialAppTracker.isSpecialApp( js.getSourceUserId(), js.getSourcePackageName())) || hasEnoughSatisfiedConstraintsLocked(js) || mService.isCurrentlyRunningLocked(js); } Loading Loading @@ -827,39 +819,6 @@ public final class FlexibilityController extends StateController { mFcConfig.processConstantLocked(properties, key); } private void registerBroadcastReceiver() { IntentFilter filter = new IntentFilter(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED); mContext.registerReceiver(mBroadcastReceiver, filter); } private void unregisterBroadcastReceiver() { mContext.unregisterReceiver(mBroadcastReceiver); } private void updatePowerAllowlistCache() { if (mDeviceIdleInternal == null) { return; } // Don't call out to DeviceIdleController with the lock held. final String[] allowlistedPkgs = mDeviceIdleInternal.getFullPowerWhitelistExceptIdle(); final ArraySet<String> changedPkgs = new ArraySet<>(); synchronized (mLock) { changedPkgs.addAll(mPowerAllowlistedApps); mPowerAllowlistedApps.clear(); for (final String pkgName : allowlistedPkgs) { mPowerAllowlistedApps.add(pkgName); if (changedPkgs.contains(pkgName)) { changedPkgs.remove(pkgName); } else { changedPkgs.add(pkgName); } } mPackagesToCheck.addAll(changedPkgs); mHandler.sendEmptyMessage(MSG_CHECK_PACKAGES); } } @VisibleForTesting class FlexibilityTracker { final ArrayList<ArraySet<JobStatus>> mTrackedJobs; Loading Loading @@ -1343,12 +1302,12 @@ public final class FlexibilityController extends StateController { mFlexibilityEnabled = true; mPrefetchController .registerPrefetchChangedListener(mPrefetchChangedListener); registerBroadcastReceiver(); mSpecialAppTracker.startTracking(); } else { mFlexibilityEnabled = false; mPrefetchController .unRegisterPrefetchChangedListener(mPrefetchChangedListener); unregisterBroadcastReceiver(); mSpecialAppTracker.stopTracking(); } } break; Loading Loading @@ -1653,6 +1612,176 @@ public final class FlexibilityController extends StateController { return mFcConfig; } private class SpecialAppTracker { /** * Lock for objects inside this class. This should never be held when attempting to acquire * {@link #mLock}. It is fine to acquire this if already holding {@link #mLock}. */ private final Object mSatLock = new Object(); private DeviceIdleInternal mDeviceIdleInternal; /** Set of all apps that have been deemed special, keyed by user ID. */ private final SparseSetArray<String> mSpecialApps = new SparseSetArray<>(); @GuardedBy("mSatLock") private final ArraySet<String> mPowerAllowlistedApps = new ArraySet<>(); private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { switch (intent.getAction()) { case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED: mHandler.post(SpecialAppTracker.this::updatePowerAllowlistCache); break; } } }; public boolean isSpecialApp(final int userId, @NonNull String packageName) { synchronized (mSatLock) { if (mSpecialApps.contains(UserHandle.USER_ALL, packageName)) { return true; } if (mSpecialApps.contains(userId, packageName)) { return true; } } return false; } private boolean isSpecialAppInternal(final int userId, @NonNull String packageName) { synchronized (mSatLock) { if (mPowerAllowlistedApps.contains(packageName)) { return true; } } return false; } private void onAppRemoved(final int userId, String packageName) { synchronized (mSatLock) { // Don't touch the USER_ALL set here. If the app is completely removed from the // device, any list that affects USER_ALL should update and this would eventually // be updated with those lists no longer containing the app. mSpecialApps.remove(userId, packageName); } } private void onSystemServicesReady() { mDeviceIdleInternal = LocalServices.getService(DeviceIdleInternal.class); synchronized (mLock) { if (mFlexibilityEnabled) { mHandler.post(SpecialAppTracker.this::updatePowerAllowlistCache); } } } private void onUserRemoved(final int userId) { synchronized (mSatLock) { mSpecialApps.remove(userId); } } private void startTracking() { IntentFilter filter = new IntentFilter( PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED); mContext.registerReceiver(mBroadcastReceiver, filter); updatePowerAllowlistCache(); } private void stopTracking() { mContext.unregisterReceiver(mBroadcastReceiver); synchronized (mSatLock) { mPowerAllowlistedApps.clear(); mSpecialApps.clear(); } } /** * Update the processed special app set for the specified user ID, only looking at the * specified set of apps. This method must <b>NEVER</b> be called while holding * {@link #mSatLock}. */ private void updateSpecialAppSetUnlocked(final int userId, @NonNull ArraySet<String> pkgs) { // This method may need to acquire mLock, so ensure that mSatLock isn't held to avoid // lock inversion. if (Thread.holdsLock(mSatLock)) { throw new IllegalStateException("Must never hold local mSatLock"); } if (pkgs.size() == 0) { return; } final ArraySet<String> changedPkgs = new ArraySet<>(); synchronized (mSatLock) { for (int i = pkgs.size() - 1; i >= 0; --i) { final String pkgName = pkgs.valueAt(i); if (isSpecialAppInternal(userId, pkgName)) { if (mSpecialApps.add(userId, pkgName)) { changedPkgs.add(pkgName); } } else if (mSpecialApps.remove(userId, pkgName)) { changedPkgs.add(pkgName); } } } if (changedPkgs.size() > 0) { synchronized (mLock) { mPackagesToCheck.addAll(changedPkgs); mHandler.sendEmptyMessage(MSG_CHECK_PACKAGES); } } } private void updatePowerAllowlistCache() { if (mDeviceIdleInternal == null) { return; } // Don't call out to DeviceIdleController with the lock held. final String[] allowlistedPkgs = mDeviceIdleInternal.getFullPowerWhitelistExceptIdle(); final ArraySet<String> changedPkgs = new ArraySet<>(); synchronized (mSatLock) { changedPkgs.addAll(mPowerAllowlistedApps); mPowerAllowlistedApps.clear(); for (String pkgName : allowlistedPkgs) { mPowerAllowlistedApps.add(pkgName); if (!changedPkgs.remove(pkgName)) { // The package wasn't in the previous set of allowlisted apps. Add it // since its state has changed. changedPkgs.add(pkgName); } } } // The full allowlist is currently user-agnostic, so use USER_ALL for these packages. updateSpecialAppSetUnlocked(UserHandle.USER_ALL, changedPkgs); } public void dump(@NonNull IndentingPrintWriter pw) { pw.println("Special apps:"); pw.increaseIndent(); synchronized (mSatLock) { for (int u = 0; u < mSpecialApps.size(); ++u) { pw.print(mSpecialApps.keyAt(u)); pw.print(": "); pw.println(mSpecialApps.valuesAt(u)); } pw.println(); pw.print("Power allowlisted packages: "); pw.println(mPowerAllowlistedApps); } pw.decreaseIndent(); } } @Override @GuardedBy("mLock") public void dumpConstants(IndentingPrintWriter pw) { Loading Loading @@ -1690,8 +1819,7 @@ public final class FlexibilityController extends StateController { pw.decreaseIndent(); pw.println(); pw.print("Power allowlisted packages: "); pw.println(mPowerAllowlistedApps); mSpecialAppTracker.dump(pw); pw.println(); mFlexibilityTracker.dump(pw, predicate, nowElapsed); Loading