Loading core/java/android/app/ActivityManagerInternal.java +10 −0 Original line number Diff line number Diff line Loading @@ -779,6 +779,16 @@ public abstract class ActivityManagerInternal { * @param started {@code true} if the process transits from non-FGS state to FGS state. */ void onForegroundServiceStateChanged(String packageName, int uid, int pid, boolean started); /** * Call when the notification of the foreground service is updated. * * @param packageName The package name of the process. * @param uid The UID of the process. * @param foregroundId The current foreground service notification ID, a negative value * means this notification is being removed. */ void onForegroundServiceNotificationUpdated(String packageName, int uid, int foregroundId); } /** Loading core/res/res/values/config.xml +6 −0 Original line number Diff line number Diff line Loading @@ -5772,4 +5772,10 @@ current drain threshold. --> <integer name="config_bg_current_drain_location_min_duration">1800</integer> <!-- The behavior for an app with a FGS and its notification is still showing, when the system detects it's abusive and should be put into bg restricted level. True - we'll show the prompt to user, False - we'll not show it. --> <bool name="config_bg_prompt_fgs_with_noti_to_bg_restricted">false</bool> </resources> core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -4747,4 +4747,5 @@ <java-symbol type="array" name="config_bg_current_drain_high_threshold_to_bg_restricted" /> <java-symbol type="integer" name="config_bg_current_drain_media_playback_min_duration" /> <java-symbol type="integer" name="config_bg_current_drain_location_min_duration" /> <java-symbol type="bool" name="config_bg_prompt_fgs_with_noti_to_bg_restricted" /> </resources> services/core/java/com/android/server/am/AppBatteryTracker.java +10 −8 Original line number Diff line number Diff line Loading @@ -1454,13 +1454,16 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> notifyController = true; } else { excessive = true; if (brPercentage >= mBgCurrentDrainBgRestrictedThreshold[thresholdIndex]) { if (brPercentage >= mBgCurrentDrainBgRestrictedThreshold[thresholdIndex] && curLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET) { // If we're in the restricted standby bucket but still seeing high // current drains, tell the controller again. if (curLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET && ts[TIME_STAMP_INDEX_BG_RESTRICTED] == 0) { if (now > ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] + mBgCurrentDrainWindowMs) { final long lastResbucket = ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET]; final long lastBgRes = ts[TIME_STAMP_INDEX_BG_RESTRICTED]; // If it has been a while since restricting the app and since the last // time we notify the controller, notify it again. if ((now >= lastResbucket + mBgCurrentDrainWindowMs) && (lastBgRes == 0 || (now >= lastBgRes + mBgCurrentDrainWindowMs))) { ts[TIME_STAMP_INDEX_BG_RESTRICTED] = now; notifyController = true; } Loading @@ -1468,7 +1471,6 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> } } } } if (excessive) { if (DEBUG_BACKGROUND_BATTERY_TRACKER) { Loading services/core/java/com/android/server/am/AppFGSTracker.java +107 −1 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; Loading @@ -52,6 +53,7 @@ import com.android.server.am.AppFGSTracker.PackageDurations; import com.android.server.am.BaseAppStateEventsTracker.BaseAppStateEventsPolicy; import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent; import com.android.server.am.BaseAppStateTracker.Injector; import com.android.server.notification.NotificationManagerInternal; import java.io.PrintWriter; import java.lang.reflect.Constructor; Loading @@ -69,6 +71,9 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac private final MyHandler mHandler; @GuardedBy("mLock") private final UidProcessMap<ArraySet<Integer>> mFGSNotificationIDs = new UidProcessMap<>(); // Unlocked since it's only accessed in single thread. private final ArrayMap<PackageDurations, Long> mTmpPkgDurations = new ArrayMap<>(); Loading Loading @@ -98,11 +103,19 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac : MyHandler.MSG_FOREGROUND_SERVICES_STOPPED, pid, uid, packageName).sendToTarget(); } @Override public void onForegroundServiceNotificationUpdated(String packageName, int uid, int foregroundId) { mHandler.obtainMessage(MyHandler.MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED, uid, foregroundId, packageName).sendToTarget(); } private static class MyHandler extends Handler { static final int MSG_FOREGROUND_SERVICES_STARTED = 0; static final int MSG_FOREGROUND_SERVICES_STOPPED = 1; static final int MSG_FOREGROUND_SERVICES_CHANGED = 2; static final int MSG_CHECK_LONG_RUNNING_FGS = 3; static final int MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED = 3; static final int MSG_CHECK_LONG_RUNNING_FGS = 4; private final AppFGSTracker mTracker; Loading @@ -126,6 +139,10 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac mTracker.handleForegroundServicesChanged( (String) msg.obj, msg.arg1, msg.arg2); break; case MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED: mTracker.handleForegroundServiceNotificationUpdated( (String) msg.obj, msg.arg1, msg.arg2); break; case MSG_CHECK_LONG_RUNNING_FGS: mTracker.checkLongRunningFgs(); break; Loading Loading @@ -203,6 +220,44 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac } } private void handleForegroundServiceNotificationUpdated(String packageName, int uid, int notificationId) { synchronized (mLock) { if (notificationId > 0) { ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName); if (notificationIDs == null) { notificationIDs = new ArraySet<>(); mFGSNotificationIDs.put(uid, packageName, notificationIDs); } notificationIDs.add(notificationId); } else if (notificationId < 0) { final ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName); if (notificationIDs != null) { notificationIDs.remove(-notificationId); if (notificationIDs.isEmpty()) { mFGSNotificationIDs.remove(uid, packageName); } } } } } @GuardedBy("mLock") private boolean hasForegroundServiceNotificationsLocked(String packageName, int uid) { final ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName); if (notificationIDs == null || notificationIDs.isEmpty()) { return false; } final NotificationManagerInternal nm = mInjector.getNotificationManagerInternal(); final int userId = UserHandle.getUserId(uid); for (int i = notificationIDs.size() - 1; i >= 0; i--) { if (nm.isNotificationShown(packageName, null, notificationIDs.valueAt(i), userId)) { return true; } } return false; } @GuardedBy("mLock") private void scheduleDurationCheckLocked(long now) { // Look for the active FGS with longest running time till now. Loading Loading @@ -373,6 +428,28 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac } } boolean hasForegroundServiceNotifications(String packageName, int uid) { synchronized (mLock) { return hasForegroundServiceNotificationsLocked(packageName, uid); } } boolean hasForegroundServiceNotifications(int uid) { synchronized (mLock) { final SparseArray<ArrayMap<String, ArraySet<Integer>>> map = mFGSNotificationIDs.getMap(); final ArrayMap<String, ArraySet<Integer>> pkgs = map.get(uid); if (pkgs != null) { for (int i = pkgs.size() - 1; i >= 0; i--) { if (hasForegroundServiceNotificationsLocked(pkgs.keyAt(i), uid)) { return true; } } } } return false; } @Override void dump(PrintWriter pw, String prefix) { pw.print(prefix); Loading @@ -380,6 +457,35 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac super.dump(pw, " " + prefix); } @Override void dumpOthers(PrintWriter pw, String prefix) { pw.print(prefix); pw.println("APPS WITH ACTIVE FOREGROUND SERVICES:"); prefix = " " + prefix; synchronized (mLock) { final SparseArray<ArrayMap<String, ArraySet<Integer>>> map = mFGSNotificationIDs.getMap(); if (map.size() == 0) { pw.print(prefix); pw.println("(none)"); } for (int i = 0, size = map.size(); i < size; i++) { final int uid = map.keyAt(i); final String uidString = UserHandle.formatUid(uid); final ArrayMap<String, ArraySet<Integer>> pkgs = map.valueAt(i); for (int j = 0, numOfPkgs = pkgs.size(); j < numOfPkgs; j++) { final String pkgName = pkgs.keyAt(j); pw.print(prefix); pw.print(pkgName); pw.print('/'); pw.print(uidString); pw.print(" notification="); pw.println(hasForegroundServiceNotificationsLocked(pkgName, uid)); } } } } /** * Tracks the durations with active FGS for a given package. */ Loading Loading
core/java/android/app/ActivityManagerInternal.java +10 −0 Original line number Diff line number Diff line Loading @@ -779,6 +779,16 @@ public abstract class ActivityManagerInternal { * @param started {@code true} if the process transits from non-FGS state to FGS state. */ void onForegroundServiceStateChanged(String packageName, int uid, int pid, boolean started); /** * Call when the notification of the foreground service is updated. * * @param packageName The package name of the process. * @param uid The UID of the process. * @param foregroundId The current foreground service notification ID, a negative value * means this notification is being removed. */ void onForegroundServiceNotificationUpdated(String packageName, int uid, int foregroundId); } /** Loading
core/res/res/values/config.xml +6 −0 Original line number Diff line number Diff line Loading @@ -5772,4 +5772,10 @@ current drain threshold. --> <integer name="config_bg_current_drain_location_min_duration">1800</integer> <!-- The behavior for an app with a FGS and its notification is still showing, when the system detects it's abusive and should be put into bg restricted level. True - we'll show the prompt to user, False - we'll not show it. --> <bool name="config_bg_prompt_fgs_with_noti_to_bg_restricted">false</bool> </resources>
core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -4747,4 +4747,5 @@ <java-symbol type="array" name="config_bg_current_drain_high_threshold_to_bg_restricted" /> <java-symbol type="integer" name="config_bg_current_drain_media_playback_min_duration" /> <java-symbol type="integer" name="config_bg_current_drain_location_min_duration" /> <java-symbol type="bool" name="config_bg_prompt_fgs_with_noti_to_bg_restricted" /> </resources>
services/core/java/com/android/server/am/AppBatteryTracker.java +10 −8 Original line number Diff line number Diff line Loading @@ -1454,13 +1454,16 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> notifyController = true; } else { excessive = true; if (brPercentage >= mBgCurrentDrainBgRestrictedThreshold[thresholdIndex]) { if (brPercentage >= mBgCurrentDrainBgRestrictedThreshold[thresholdIndex] && curLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET) { // If we're in the restricted standby bucket but still seeing high // current drains, tell the controller again. if (curLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET && ts[TIME_STAMP_INDEX_BG_RESTRICTED] == 0) { if (now > ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] + mBgCurrentDrainWindowMs) { final long lastResbucket = ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET]; final long lastBgRes = ts[TIME_STAMP_INDEX_BG_RESTRICTED]; // If it has been a while since restricting the app and since the last // time we notify the controller, notify it again. if ((now >= lastResbucket + mBgCurrentDrainWindowMs) && (lastBgRes == 0 || (now >= lastBgRes + mBgCurrentDrainWindowMs))) { ts[TIME_STAMP_INDEX_BG_RESTRICTED] = now; notifyController = true; } Loading @@ -1468,7 +1471,6 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> } } } } if (excessive) { if (DEBUG_BACKGROUND_BATTERY_TRACKER) { Loading
services/core/java/com/android/server/am/AppFGSTracker.java +107 −1 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; Loading @@ -52,6 +53,7 @@ import com.android.server.am.AppFGSTracker.PackageDurations; import com.android.server.am.BaseAppStateEventsTracker.BaseAppStateEventsPolicy; import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent; import com.android.server.am.BaseAppStateTracker.Injector; import com.android.server.notification.NotificationManagerInternal; import java.io.PrintWriter; import java.lang.reflect.Constructor; Loading @@ -69,6 +71,9 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac private final MyHandler mHandler; @GuardedBy("mLock") private final UidProcessMap<ArraySet<Integer>> mFGSNotificationIDs = new UidProcessMap<>(); // Unlocked since it's only accessed in single thread. private final ArrayMap<PackageDurations, Long> mTmpPkgDurations = new ArrayMap<>(); Loading Loading @@ -98,11 +103,19 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac : MyHandler.MSG_FOREGROUND_SERVICES_STOPPED, pid, uid, packageName).sendToTarget(); } @Override public void onForegroundServiceNotificationUpdated(String packageName, int uid, int foregroundId) { mHandler.obtainMessage(MyHandler.MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED, uid, foregroundId, packageName).sendToTarget(); } private static class MyHandler extends Handler { static final int MSG_FOREGROUND_SERVICES_STARTED = 0; static final int MSG_FOREGROUND_SERVICES_STOPPED = 1; static final int MSG_FOREGROUND_SERVICES_CHANGED = 2; static final int MSG_CHECK_LONG_RUNNING_FGS = 3; static final int MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED = 3; static final int MSG_CHECK_LONG_RUNNING_FGS = 4; private final AppFGSTracker mTracker; Loading @@ -126,6 +139,10 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac mTracker.handleForegroundServicesChanged( (String) msg.obj, msg.arg1, msg.arg2); break; case MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED: mTracker.handleForegroundServiceNotificationUpdated( (String) msg.obj, msg.arg1, msg.arg2); break; case MSG_CHECK_LONG_RUNNING_FGS: mTracker.checkLongRunningFgs(); break; Loading Loading @@ -203,6 +220,44 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac } } private void handleForegroundServiceNotificationUpdated(String packageName, int uid, int notificationId) { synchronized (mLock) { if (notificationId > 0) { ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName); if (notificationIDs == null) { notificationIDs = new ArraySet<>(); mFGSNotificationIDs.put(uid, packageName, notificationIDs); } notificationIDs.add(notificationId); } else if (notificationId < 0) { final ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName); if (notificationIDs != null) { notificationIDs.remove(-notificationId); if (notificationIDs.isEmpty()) { mFGSNotificationIDs.remove(uid, packageName); } } } } } @GuardedBy("mLock") private boolean hasForegroundServiceNotificationsLocked(String packageName, int uid) { final ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName); if (notificationIDs == null || notificationIDs.isEmpty()) { return false; } final NotificationManagerInternal nm = mInjector.getNotificationManagerInternal(); final int userId = UserHandle.getUserId(uid); for (int i = notificationIDs.size() - 1; i >= 0; i--) { if (nm.isNotificationShown(packageName, null, notificationIDs.valueAt(i), userId)) { return true; } } return false; } @GuardedBy("mLock") private void scheduleDurationCheckLocked(long now) { // Look for the active FGS with longest running time till now. Loading Loading @@ -373,6 +428,28 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac } } boolean hasForegroundServiceNotifications(String packageName, int uid) { synchronized (mLock) { return hasForegroundServiceNotificationsLocked(packageName, uid); } } boolean hasForegroundServiceNotifications(int uid) { synchronized (mLock) { final SparseArray<ArrayMap<String, ArraySet<Integer>>> map = mFGSNotificationIDs.getMap(); final ArrayMap<String, ArraySet<Integer>> pkgs = map.get(uid); if (pkgs != null) { for (int i = pkgs.size() - 1; i >= 0; i--) { if (hasForegroundServiceNotificationsLocked(pkgs.keyAt(i), uid)) { return true; } } } } return false; } @Override void dump(PrintWriter pw, String prefix) { pw.print(prefix); Loading @@ -380,6 +457,35 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac super.dump(pw, " " + prefix); } @Override void dumpOthers(PrintWriter pw, String prefix) { pw.print(prefix); pw.println("APPS WITH ACTIVE FOREGROUND SERVICES:"); prefix = " " + prefix; synchronized (mLock) { final SparseArray<ArrayMap<String, ArraySet<Integer>>> map = mFGSNotificationIDs.getMap(); if (map.size() == 0) { pw.print(prefix); pw.println("(none)"); } for (int i = 0, size = map.size(); i < size; i++) { final int uid = map.keyAt(i); final String uidString = UserHandle.formatUid(uid); final ArrayMap<String, ArraySet<Integer>> pkgs = map.valueAt(i); for (int j = 0, numOfPkgs = pkgs.size(); j < numOfPkgs; j++) { final String pkgName = pkgs.keyAt(j); pw.print(prefix); pw.print(pkgName); pw.print('/'); pw.print(uidString); pw.print(" notification="); pw.println(hasForegroundServiceNotificationsLocked(pkgName, uid)); } } } } /** * Tracks the durations with active FGS for a given package. */ Loading