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

Commit a6b92260 authored by Hui Yu's avatar Hui Yu
Browse files

Do not allow FGS start when temp allowlist reasonCode is REASON_PUSH_MESSAGING_OVER_QUOTA

If temp allowlist reasonCode is REASON_PUSH_MESSAGING_OVER_QUOTA, check
DeviceConfig key "push_messaging_over_quota_behavior" to decide if
temp allowlist and FGS start are allowed. Three options:
1. -1, temp allowlist not allowed, FGS start not allowed.
2. 0, temp allowlist allowed, FGS start allowed.
3. 1, temp allowlist allowed, FGS start not allowed. This is default.

The device config command to change the behavior:
adb shell device_config set activity_manager push_messaging_over_quota_behavior <-1|0|1>

If temp allowlist reasonCode is REASON_DENIED, do not allow temp allowlist
at all.

Also, in DeviceIdleController.addPowerSaveTempAllowlistAppInternal(),
there used to be a check if the callingUid is on the mPowerSaveWhitelistSystemAppIds list,
this is unnecessary because upstream callers already checked if callingUid has
 permission CHANGE_DEVICE_IDLE_TEMP_WHITELIST, and this permission is a privileged permission,
so removing the check of mPowerSaveWhitelistSystemAppIds should be safe.

Bug: 182796372
Test: atest cts/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java#testPushMessagingOverQuota
Test: atest cts/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java#testTempAllowListReasonCode

Change-Id: Id34b1c26c819dc4fe07838eb2e3a8f0138cbcf8f
parent 329a11f7
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -96,4 +96,13 @@ public interface DeviceIdleInternal {
     * that the device is stationary or in motion.
     */
    void unregisterStationaryListener(StationaryListener listener);

    /**
     * Apply some restrictions on temp allowlist type based on the reasonCode.
     * @param reasonCode temp allowlist reason code.
     * @param defaultType default temp allowlist type if reasonCode can not decide a type.
     * @return temp allowlist type based on the reasonCode.
     */
    @TempAllowListType int getTempAllowListType(@ReasonCode int reasonCode,
            @TempAllowListType int defaultType);
}
+26 −13
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.server;
import static android.os.PowerExemptionManager.REASON_SHELL;
import static android.os.PowerExemptionManager.REASON_UNKNOWN;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_NONE;
import static android.os.Process.INVALID_UID;

import android.Manifest;
@@ -58,6 +59,7 @@ import android.os.Handler;
import android.os.IDeviceIdleController;
import android.os.Looper;
import android.os.Message;
import android.os.PowerExemptionManager;
import android.os.PowerExemptionManager.ReasonCode;
import android.os.PowerExemptionManager.TempAllowListType;
import android.os.PowerManager;
@@ -2015,6 +2017,12 @@ public class DeviceIdleController extends SystemService
        public void unregisterStationaryListener(StationaryListener listener) {
            DeviceIdleController.this.unregisterStationaryListener(listener);
        }

        @Override
        public @TempAllowListType int getTempAllowListType(@ReasonCode int reasonCode,
                @TempAllowListType int defaultType) {
            return DeviceIdleController.this.getTempAllowListType(reasonCode, defaultType);
        }
    }

    private class LocalPowerAllowlistService implements PowerAllowlistInternal {
@@ -2689,6 +2697,18 @@ public class DeviceIdleController extends SystemService
        }
    }

    private @TempAllowListType int getTempAllowListType(@ReasonCode int reasonCode,
            @TempAllowListType int defaultType) {
        switch (reasonCode) {
            case PowerExemptionManager.REASON_PUSH_MESSAGING_OVER_QUOTA:
                return mLocalActivityManager.getPushMessagingOverQuotaBehavior();
            case PowerExemptionManager.REASON_DENIED:
                return TEMPORARY_ALLOW_LIST_TYPE_NONE;
            default:
                return defaultType;
        }
    }

    void addPowerSaveTempAllowlistAppChecked(String packageName, long duration,
            int userId, @ReasonCode int reasonCode, @Nullable String reason)
            throws RemoteException {
@@ -2705,9 +2725,12 @@ public class DeviceIdleController extends SystemService
                "addPowerSaveTempWhitelistApp", null);
        final long token = Binder.clearCallingIdentity();
        try {
            @TempAllowListType int type = getTempAllowListType(reasonCode,
                    TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED);
            if (type != TEMPORARY_ALLOW_LIST_TYPE_NONE) {
                addPowerSaveTempAllowlistAppInternal(callingUid,
                    packageName, duration, TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
                    userId, true, reasonCode, reason);
                        packageName, duration, type, userId, true, reasonCode, reason);
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
@@ -2741,16 +2764,6 @@ public class DeviceIdleController extends SystemService
    void addPowerSaveTempAllowlistAppInternal(int callingUid, String packageName,
            long durationMs, @TempAllowListType int tempAllowListType, int userId, boolean sync,
            @ReasonCode int reasonCode, @Nullable String reason) {
        synchronized (this) {
            int callingAppId = UserHandle.getAppId(callingUid);
            if (callingAppId >= Process.FIRST_APPLICATION_UID) {
                if (!mPowerSaveWhitelistSystemAppIds.get(callingAppId)) {
                    throw new SecurityException(
                            "Calling app " + UserHandle.formatUid(callingUid)
                                    + " is not on whitelist");
                }
            }
        }
        try {
            int uid = getContext().getPackageManager().getPackageUidAsUser(packageName, userId);
            addPowerSaveTempWhitelistAppDirectInternal(callingUid, uid, durationMs,
+5 −0
Original line number Diff line number Diff line
@@ -583,4 +583,9 @@ public abstract class ActivityManagerInternal {
     * Is the FGS started from an uid temporarily allowed to have while-in-use permission?
     */
    public abstract boolean isTempAllowlistedForFgsWhileInUse(int uid);

    /**
     * Return the temp allowlist type when server push messaging is over the quota.
     */
    public abstract @TempAllowListType int getPushMessagingOverQuotaBehavior();
}
+41 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.server.am;

import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_NONE;

import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK;

import android.app.ActivityThread;
@@ -27,6 +30,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.os.PowerExemptionManager;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.OnPropertiesChangedListener;
import android.provider.DeviceConfig.Properties;
@@ -139,6 +143,11 @@ final class ActivityManagerConstants extends ContentObserver {
    private static final long DEFAULT_FG_TO_BG_FGS_GRACE_DURATION = 5 * 1000;
    private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000;
    private static final float DEFAULT_FGS_ATOM_SAMPLE_RATE = 1; // 100 %
    /**
     * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
     */
    private static final int
            DEFAULT_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR = 1;

    // Flag stored in the DeviceConfig API.
    /**
@@ -210,6 +219,13 @@ final class ActivityManagerConstants extends ContentObserver {
    private static final String KEY_DEFERRED_FGS_NOTIFICATION_EXCLUSION_TIME =
            "deferred_fgs_notification_exclusion_time";

    /**
     * Default value for mPushMessagingOverQuotaBehavior if not explicitly set in
     * Settings.Global.
     */
    private static final String KEY_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR =
            "push_messaging_over_quota_behavior";

    // Maximum number of cached processes we will allow.
    public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES;

@@ -413,6 +429,13 @@ final class ActivityManagerConstants extends ContentObserver {
    // before another FGS notifiction from that app can be deferred.
    volatile long mFgsNotificationDeferralExclusionTime = 2 * 60 * 1000L;

    /**
     * When server pushing message is over the quote, select one of the temp allow list type as
     * defined in {@link PowerExemptionManager.TempAllowListType}
     */
    volatile @PowerExemptionManager.TempAllowListType int mPushMessagingOverQuotaBehavior =
            DEFAULT_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR;

    /*
     * At boot time, broadcast receiver ACTION_BOOT_COMPLETED, ACTION_LOCKED_BOOT_COMPLETED and
     * ACTION_PRE_BOOT_COMPLETED are temp allowlisted to start FGS for a duration of time in
@@ -605,6 +628,9 @@ final class ActivityManagerConstants extends ContentObserver {
                            case KEY_DEFERRED_FGS_NOTIFICATION_EXCLUSION_TIME:
                                updateFgsNotificationDeferralExclusionTime();
                                break;
                            case KEY_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR:
                                updatePushMessagingOverQuotaBehavior();
                                break;
                            case KEY_OOMADJ_UPDATE_POLICY:
                                updateOomAdjUpdatePolicy();
                                break;
@@ -909,6 +935,19 @@ final class ActivityManagerConstants extends ContentObserver {
                /*default value*/ 2 * 60 * 1000L);
    }

    private void updatePushMessagingOverQuotaBehavior() {
        mPushMessagingOverQuotaBehavior = DeviceConfig.getInt(
                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR,
                DEFAULT_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR);
        if (mPushMessagingOverQuotaBehavior < TEMPORARY_ALLOW_LIST_TYPE_NONE
                || mPushMessagingOverQuotaBehavior
                > TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED) {
            mPushMessagingOverQuotaBehavior =
                    DEFAULT_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR;
        }
    }

    private void updateOomAdjUpdatePolicy() {
        OOMADJ_UPDATE_QUICK = DeviceConfig.getInt(
                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1166,6 +1205,8 @@ final class ActivityManagerConstants extends ContentObserver {
        pw.print("="); pw.println(mFgsStartRestrictionCheckCallerTargetSdk);
        pw.print("  "); pw.print(KEY_FGS_ATOM_SAMPLE_RATE);
        pw.print("="); pw.println(mDefaultFgsAtomSampleRate);
        pw.print("  "); pw.print(KEY_PUSH_MESSAGING_OVER_QUOTA_BEHAVIOR);
        pw.print("="); pw.println(mPushMessagingOverQuotaBehavior);

        pw.println();
        if (mOverrideMaxCachedProcesses >= 0) {
+17 −4
Original line number Diff line number Diff line
@@ -51,7 +51,8 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
import static android.os.IServiceManager.DUMP_FLAG_PROTO;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_NONE;
import static android.os.Process.BLUETOOTH_UID;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.INVALID_UID;
@@ -14603,15 +14604,20 @@ public class ActivityManagerService extends IActivityManager.Stub
     */
    @GuardedBy("this")
    void tempAllowlistUidLocked(int targetUid, long duration, @ReasonCode int reasonCode,
            String reason, int type, int callingUid) {
            String reason, @TempAllowListType int type, int callingUid) {
        synchronized (mProcLock) {
            // The temp allowlist type could change according to the reasonCode.
            type = mLocalDeviceIdleController.getTempAllowListType(reasonCode, type);
            if (type == TEMPORARY_ALLOW_LIST_TYPE_NONE) {
                return;
            }
            mPendingTempAllowlist.put(targetUid,
                    new PendingTempAllowlist(targetUid, duration, reasonCode, reason, type,
                            callingUid));
            setUidTempAllowlistStateLSP(targetUid, true);
            mUiHandler.obtainMessage(PUSH_TEMP_ALLOWLIST_UI_MSG).sendToTarget();
            if (type == TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
            if (type == TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
                mFgsStartTempAllowList.add(targetUid, duration,
                        new FgsTempAllowListItem(duration, reasonCode, reason, callingUid));
            }
@@ -15285,7 +15291,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                synchronized (mProcLock) {
                    mDeviceIdleTempAllowlist = appids;
                    if (adding) {
                        if (type == TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
                        if (type == TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
                            mFgsStartTempAllowList.add(changingUid, durationMs,
                                    new FgsTempAllowListItem(durationMs, reasonCode, reason,
                                    callingUid));
@@ -16152,6 +16158,13 @@ public class ActivityManagerService extends IActivityManager.Stub
                return mServices.canAllowWhileInUsePermissionInFgsLocked(pid, uid, packageName);
            }
        }
        @Override
        public @TempAllowListType int getPushMessagingOverQuotaBehavior() {
            synchronized (ActivityManagerService.this) {
                return mConstants.mPushMessagingOverQuotaBehavior;
            }
        }
    }
    long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {