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

Commit 9aeeb35e authored by Jing Ji's avatar Jing Ji Committed by Android (Google) Code Review
Browse files

Merge "Add support to exempt the battery usage with visible FGS notifications" into tm-dev

parents 5ec218ab 7f1f56b0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -5796,7 +5796,7 @@
    <!-- The types of state where we'll exempt its battery usage during that state.
         The state here must be one or a combination of STATE_TYPE_* in BaseAppStateTracker.
    -->
    <integer name="config_bg_current_drain_exempted_types">9</integer>
    <integer name="config_bg_current_drain_exempted_types">25</integer>

    <!-- The behavior when an app has the permission ACCESS_BACKGROUND_LOCATION granted,
         whether or not the system will use a higher threshold towards its background battery usage
+138 −19
Original line number Diff line number Diff line
@@ -32,18 +32,22 @@ import static com.android.server.am.BaseAppStateTracker.ONE_HOUR;
import android.annotation.NonNull;
import android.app.ActivityManagerInternal.ForegroundServiceStateListener;
import android.app.IProcessObserver;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ServiceInfo.ForegroundServiceType;
import android.os.Handler;
import android.os.Message;
import android.os.PowerExemptionManager.ReasonCode;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.TimeUtils;

import com.android.internal.annotations.GuardedBy;
@@ -53,7 +57,6 @@ 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;
@@ -72,11 +75,14 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
    private final MyHandler mHandler;

    @GuardedBy("mLock")
    private final UidProcessMap<ArraySet<Integer>> mFGSNotificationIDs = new UidProcessMap<>();
    private final UidProcessMap<SparseBooleanArray> mFGSNotificationIDs = new UidProcessMap<>();

    // Unlocked since it's only accessed in single thread.
    private final ArrayMap<PackageDurations, Long> mTmpPkgDurations = new ArrayMap<>();

    @VisibleForTesting
    final NotificationListener mNotificationListener = new NotificationListener();

    final IProcessObserver.Stub mProcessObserver = new IProcessObserver.Stub() {
        @Override
        public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) {
@@ -116,6 +122,8 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
        static final int MSG_FOREGROUND_SERVICES_CHANGED = 2;
        static final int MSG_FOREGROUND_SERVICES_NOTIFICATION_UPDATED = 3;
        static final int MSG_CHECK_LONG_RUNNING_FGS = 4;
        static final int MSG_NOTIFICATION_POSTED = 5;
        static final int MSG_NOTIFICATION_REMOVED = 6;

        private final AppFGSTracker mTracker;

@@ -146,6 +154,12 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
                case MSG_CHECK_LONG_RUNNING_FGS:
                    mTracker.checkLongRunningFgs();
                    break;
                case MSG_NOTIFICATION_POSTED:
                    mTracker.handleNotificationPosted((String) msg.obj, msg.arg1, msg.arg2);
                    break;
                case MSG_NOTIFICATION_REMOVED:
                    mTracker.handleNotificationRemoved((String) msg.obj, msg.arg1, msg.arg2);
                    break;
            }
        }
    }
@@ -223,20 +237,37 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
    private void handleForegroundServiceNotificationUpdated(String packageName, int uid,
            int notificationId) {
        synchronized (mLock) {
            SparseBooleanArray notificationIDs = mFGSNotificationIDs.get(uid, packageName);
            if (notificationId > 0) {
                ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName);
                if (notificationIDs == null) {
                    notificationIDs = new ArraySet<>();
                    notificationIDs = new SparseBooleanArray();
                    mFGSNotificationIDs.put(uid, packageName, notificationIDs);
                }
                notificationIDs.add(notificationId);
                notificationIDs.put(notificationId, false);
            } else if (notificationId < 0) {
                final ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName);
                if (notificationIDs != null) {
                    notificationIDs.remove(-notificationId);
                    if (notificationIDs.isEmpty()) {
                    final int indexOfKey = notificationIDs.indexOfKey(-notificationId);
                    if (indexOfKey >= 0) {
                        final boolean wasVisible = notificationIDs.valueAt(indexOfKey);
                        notificationIDs.removeAt(indexOfKey);
                        if (notificationIDs.size() == 0) {
                            mFGSNotificationIDs.remove(uid, packageName);
                        }
                        // Walk through the list of FGS notification IDs and see if there are any
                        // visible ones.
                        for (int i = notificationIDs.size() - 1; i >= 0; i--) {
                            if (notificationIDs.valueAt(i)) {
                                // Still visible, nothing to do.
                                return;
                            }
                        }
                        if (wasVisible) {
                            // That was the last visible notification, notify the listeners.
                            notifyListenersOnStateChange(uid, packageName, false,
                                    SystemClock.elapsedRealtime(),
                                    STATE_TYPE_FGS_WITH_NOTIFICATION);
                        }
                    }
                }
            }
        }
@@ -244,20 +275,74 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac

    @GuardedBy("mLock")
    private boolean hasForegroundServiceNotificationsLocked(String packageName, int uid) {
        final ArraySet<Integer> notificationIDs = mFGSNotificationIDs.get(uid, packageName);
        if (notificationIDs == null || notificationIDs.isEmpty()) {
        final SparseBooleanArray notificationIDs = mFGSNotificationIDs.get(uid, packageName);
        if (notificationIDs == null || notificationIDs.size() == 0) {
            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)) {
            if (notificationIDs.valueAt(i)) {
                return true;
            }
        }
        return false;
    }

    private void handleNotificationPosted(String pkgName, int uid, int notificationId) {
        synchronized (mLock) {
            final SparseBooleanArray notificationIDs = mFGSNotificationIDs.get(uid, pkgName);
            final int indexOfKey;
            if (notificationIDs == null
                    || (indexOfKey = notificationIDs.indexOfKey(notificationId)) < 0) {
                return;
            }
            if (notificationIDs.valueAt(indexOfKey)) {
                // It's already visible.
                return;
            }
            boolean anyVisible = false;
            // Walk through the list of FGS notification IDs and see if there are any visible ones.
            for (int i = notificationIDs.size() - 1; i >= 0; i--) {
                if (notificationIDs.valueAt(i)) {
                    anyVisible = true;
                    break;
                }
            }
            notificationIDs.setValueAt(indexOfKey, true);
            if (!anyVisible) {
                // We didn't have any visible FGS notifications but now we have one,
                // let the listeners know.
                notifyListenersOnStateChange(uid, pkgName, true, SystemClock.elapsedRealtime(),
                        STATE_TYPE_FGS_WITH_NOTIFICATION);
            }
        }
    }

    private void handleNotificationRemoved(String pkgName, int uid, int notificationId) {
        synchronized (mLock) {
            final SparseBooleanArray notificationIDs = mFGSNotificationIDs.get(uid, pkgName);
            final int indexOfKey;
            if (notificationIDs == null
                    || (indexOfKey = notificationIDs.indexOfKey(notificationId)) < 0) {
                return;
            }
            if (!notificationIDs.valueAt(indexOfKey)) {
                // It's already invisible.
                return;
            }
            notificationIDs.setValueAt(indexOfKey, false);
            // Walk through the list of FGS notification IDs and see if there are any visible ones.
            for (int i = notificationIDs.size() - 1; i >= 0; i--) {
                if (notificationIDs.valueAt(i)) {
                    // Still visible, nothing to do.
                    return;
                }
            }
            // Nothing is visible now, let the listeners know.
            notifyListenersOnStateChange(uid, pkgName, false, SystemClock.elapsedRealtime(),
                    STATE_TYPE_FGS_WITH_NOTIFICATION);
        }
    }

    @GuardedBy("mLock")
    private void scheduleDurationCheckLocked(long now) {
        // Look for the active FGS with longest running time till now.
@@ -374,7 +459,19 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
            synchronized (mLock) {
                scheduleDurationCheckLocked(SystemClock.elapsedRealtime());
            }
            try {
                mNotificationListener.registerAsSystemService(mContext,
                        new ComponentName(mContext, NotificationListener.class),
                        UserHandle.USER_ALL);
            } catch (RemoteException e) {
                // Intra-process call, should never happen.
            }
        } else {
            try {
                mNotificationListener.unregisterAsSystemService();
            } catch (RemoteException e) {
                // Intra-process call, should never happen.
            }
            mHandler.removeMessages(MyHandler.MSG_CHECK_LONG_RUNNING_FGS);
            synchronized (mLock) {
                mPkgEvents.clear();
@@ -436,9 +533,9 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac

    boolean hasForegroundServiceNotifications(int uid) {
        synchronized (mLock) {
            final SparseArray<ArrayMap<String, ArraySet<Integer>>> map =
            final SparseArray<ArrayMap<String, SparseBooleanArray>> map =
                    mFGSNotificationIDs.getMap();
            final ArrayMap<String, ArraySet<Integer>> pkgs = map.get(uid);
            final ArrayMap<String, SparseBooleanArray> pkgs = map.get(uid);
            if (pkgs != null) {
                for (int i = pkgs.size() - 1; i >= 0; i--) {
                    if (hasForegroundServiceNotificationsLocked(pkgs.keyAt(i), uid)) {
@@ -463,7 +560,7 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
        pw.println("APPS WITH ACTIVE FOREGROUND SERVICES:");
        prefix = "  " + prefix;
        synchronized (mLock) {
            final SparseArray<ArrayMap<String, ArraySet<Integer>>> map =
            final SparseArray<ArrayMap<String, SparseBooleanArray>> map =
                    mFGSNotificationIDs.getMap();
            if (map.size() == 0) {
                pw.print(prefix);
@@ -472,7 +569,7 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
            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);
                final ArrayMap<String, SparseBooleanArray> pkgs = map.valueAt(i);
                for (int j = 0, numOfPkgs = pkgs.size(); j < numOfPkgs; j++) {
                    final String pkgName = pkgs.keyAt(j);
                    pw.print(prefix);
@@ -622,6 +719,28 @@ final class AppFGSTracker extends BaseAppStateDurationsTracker<AppFGSPolicy, Pac
        }
    }

    @VisibleForTesting
    class NotificationListener extends NotificationListenerService {
        @Override
        public void onNotificationPosted(StatusBarNotification sbn, RankingMap map) {
            if (DEBUG_BACKGROUND_FGS_TRACKER) {
                Slog.i(TAG, "Notification posted: " + sbn);
            }
            mHandler.obtainMessage(MyHandler.MSG_NOTIFICATION_POSTED,
                    sbn.getUid(), sbn.getId(), sbn.getPackageName()).sendToTarget();
        }

        @Override
        public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
                int reason) {
            if (DEBUG_BACKGROUND_FGS_TRACKER) {
                Slog.i(TAG, "Notification removed: " + sbn);
            }
            mHandler.obtainMessage(MyHandler.MSG_NOTIFICATION_REMOVED,
                    sbn.getUid(), sbn.getId(), sbn.getPackageName()).sendToTarget();
        }
    }

    static final class AppFGSPolicy extends BaseAppStateEventsPolicy<AppFGSTracker> {
        /**
         * Whether or not we should enable the monitoring on abusive FGS.
+8 −3
Original line number Diff line number Diff line
@@ -64,13 +64,15 @@ public abstract class BaseAppStateTracker<T extends BaseAppStatePolicy> {
    static final int STATE_TYPE_MEDIA_SESSION = 1;
    static final int STATE_TYPE_FGS_MEDIA_PLAYBACK = 1 << 1;
    static final int STATE_TYPE_FGS_LOCATION = 1 << 2;
    static final int STATE_TYPE_PERMISSION = 1 << 3;
    static final int STATE_TYPE_NUM = 4;
    static final int STATE_TYPE_FGS_WITH_NOTIFICATION = 1 << 3;
    static final int STATE_TYPE_PERMISSION = 1 << 4;
    static final int STATE_TYPE_NUM = 5;

    static final int STATE_TYPE_INDEX_MEDIA_SESSION = 0;
    static final int STATE_TYPE_INDEX_FGS_MEDIA_PLAYBACK = 1;
    static final int STATE_TYPE_INDEX_FGS_LOCATION = 2;
    static final int STATE_TYPE_INDEX_PERMISSION = 3;
    static final int STATE_TYPE_INDEX_FGS_WITH_NOTIFICATION = 3;
    static final int STATE_TYPE_INDEX_PERMISSION = 4;

    protected final AppRestrictionController mAppRestrictionController;
    protected final Injector<T> mInjector;
@@ -129,6 +131,9 @@ public abstract class BaseAppStateTracker<T extends BaseAppStatePolicy> {
                case STATE_TYPE_FGS_LOCATION:
                    sb.append("FGS_LOCATION");
                    break;
                case STATE_TYPE_FGS_WITH_NOTIFICATION:
                    sb.append("FGS_NOTIFICATION");
                    break;
                case STATE_TYPE_PERMISSION:
                    sb.append("PERMISSION");
                    break;
+1 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission
        android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />

    <!-- needed by MasterClearReceiverTest to display a system dialog -->
    <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
+109 −39

File changed.

Preview size limit exceeded, changes collapsed.