Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8ddcda8b authored by Jing Ji's avatar Jing Ji
Browse files

Don't prompt on bg abuse if the app has a FGS with notification

Add a new config to control whether or not to prompt the user when
the system detects a background abusive app and it has a foreground
service with notification shown. By default it's OFF, meaning if
the background abusive app has a foreground service and its
notification is still showing, no prompt to the user in this case.

Bug: 200326767
Bug: 203105544
Test: atest FrameworksMockingServicesTests:BackgroundRestrictionTest
Change-Id: Ic611090e3152117a2379d902fc32416e11c3e197
parent ed6be441
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -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);
    }

    /**
+6 −0
Original line number Diff line number Diff line
@@ -5758,4 +5758,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>
+1 −0
Original line number Diff line number Diff line
@@ -4743,4 +4743,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>
+10 −8
Original line number Diff line number Diff line
@@ -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;
                            }
@@ -1468,7 +1471,6 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy>
                    }
                }
            }
            }

            if (excessive) {
                if (DEBUG_BACKGROUND_BATTERY_TRACKER) {
+107 −1
Original line number Diff line number Diff line
@@ -43,6 +43,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;
@@ -54,6 +55,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;
@@ -71,6 +73,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<>();

@@ -100,11 +105,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;

@@ -128,6 +141,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;
@@ -205,6 +222,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.
@@ -375,6 +430,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);
@@ -382,6 +459,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