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

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

All FGS exemptions should be propagated via service bindings.

Previously, the BG-FGS-start capability can be passed down over service
binding, this was implemented in OomAdjuster, due to performance reason,
only an subset of FGS exemptions are passed down.

This new implementation move it out of OomAdjuster and can pass all FGS
exemptions. Instead of in OomAdjuster it can passed down over infinite
number of levels of chain binding, the new implementation will be passed
down one level.

First we call shouldAllowFgsStartForegroundLocked() on the callingUid
that starts the FGS, if the return is DENIED, we check if callingUid has
any service that is bound by another UID(clientUid), we call
shouldAllowFgsStartForegroundLocked() on clientUid.

To check if a BG-FGS-start is allowed by any clientUid over service
binding, looking for ""Background started FGS: " string in logcat, if
the "clientCallingPackage" field is non-null, the reason BG-FGS-start is
because of a clientUid and clientCallingPackage is clientUid's
packageName.

Bug: 188086703
Test: atest cts/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java#testFgsBindingFlagNone
Change-Id: Ia692201909cc5095f4aa25ba5a1588510054fb80
parent fc0a05e3
Loading
Loading
Loading
Loading
+93 −30
Original line number Diff line number Diff line
@@ -21,38 +21,40 @@ import static android.Manifest.permission.REQUEST_COMPANION_START_FOREGROUND_SER
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
import static android.app.ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST;
import static android.os.PowerExemptionManager.REASON_ACTIVITY_STARTER;
import static android.os.PowerExemptionManager.REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD;
import static android.os.PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE;
import static android.os.PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
import static android.os.PowerExemptionManager.REASON_BACKGROUND_FGS_PERMISSION;
import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER;
import static android.os.PowerExemptionManager.REASON_DENIED;
import static android.os.PowerExemptionManager.REASON_DEVICE_DEMO_MODE;
import static android.os.PowerExemptionManager.REASON_DEVICE_OWNER;
import static android.os.PowerExemptionManager.REASON_FGS_BINDING;
import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_FGS_PERMISSION;
import static android.os.PowerExemptionManager.REASON_OPT_OUT_REQUESTED;
import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_PLATFORM_VPN;
import static android.os.PowerExemptionManager.REASON_OP_ACTIVATE_VPN;
import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT;
import static android.os.PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI;
import static android.os.PowerExemptionManager.REASON_PROC_STATE_TOP;
import static android.os.PowerExemptionManager.REASON_PROFILE_OWNER;
import static android.os.PowerExemptionManager.REASON_SERVICE_LAUNCH;
import static android.os.PowerExemptionManager.REASON_START_ACTIVITY_FLAG;
import static android.os.PowerExemptionManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
import static android.os.PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED;
import static android.os.PowerExemptionManager.REASON_SYSTEM_UID;
import static android.os.PowerExemptionManager.REASON_TEMP_ALLOWED_WHILE_IN_USE;
import static android.os.PowerWhitelistManager.REASON_ACTIVITY_STARTER;
import static android.os.PowerWhitelistManager.REASON_ALLOWLISTED_PACKAGE;
import static android.os.PowerWhitelistManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
import static android.os.PowerWhitelistManager.REASON_BACKGROUND_FGS_PERMISSION;
import static android.os.PowerWhitelistManager.REASON_COMPANION_DEVICE_MANAGER;
import static android.os.PowerWhitelistManager.REASON_DENIED;
import static android.os.PowerWhitelistManager.REASON_DEVICE_DEMO_MODE;
import static android.os.PowerWhitelistManager.REASON_DEVICE_OWNER;
import static android.os.PowerWhitelistManager.REASON_FGS_BINDING;
import static android.os.PowerWhitelistManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
import static android.os.PowerWhitelistManager.REASON_INSTR_BACKGROUND_FGS_PERMISSION;
import static android.os.PowerWhitelistManager.REASON_PROC_STATE_PERSISTENT;
import static android.os.PowerWhitelistManager.REASON_PROC_STATE_PERSISTENT_UI;
import static android.os.PowerWhitelistManager.REASON_PROC_STATE_TOP;
import static android.os.PowerWhitelistManager.REASON_PROFILE_OWNER;
import static android.os.PowerWhitelistManager.REASON_START_ACTIVITY_FLAG;
import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED;
import static android.os.PowerWhitelistManager.REASON_SYSTEM_UID;
import static android.os.PowerWhitelistManager.REASON_UID_VISIBLE;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.PowerWhitelistManager.getReasonCodeFromProcState;
import static android.os.PowerWhitelistManager.reasonCodeToString;
import static android.os.PowerExemptionManager.REASON_UID_VISIBLE;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.PowerExemptionManager.getReasonCodeFromProcState;
import static android.os.PowerExemptionManager.reasonCodeToString;
import static android.os.Process.INVALID_UID;
import static android.os.Process.NFC_UID;
import static android.os.Process.ROOT_UID;
@@ -118,8 +120,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerWhitelistManager;
import android.os.PowerWhitelistManager.ReasonCode;
import android.os.PowerExemptionManager.ReasonCode;
import android.os.Process;
import android.os.RemoteCallback;
import android.os.RemoteException;
@@ -135,6 +136,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
@@ -3614,8 +3616,9 @@ public final class ActiveServices {
                        + " for fg-service launch");
            }
            mAm.tempAllowlistUidLocked(r.appInfo.uid,
                    SERVICE_START_FOREGROUND_TIMEOUT, PowerWhitelistManager.REASON_SERVICE_LAUNCH,
                    "fg-service-launch", TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
                    SERVICE_START_FOREGROUND_TIMEOUT, REASON_SERVICE_LAUNCH,
                    "fg-service-launch",
                    TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
                    r.mRecentCallingUid);
        }

@@ -5740,6 +5743,66 @@ public final class ActiveServices {
        int ret = shouldAllowFgsStartForegroundLocked(allowWhileInUse, callingPid, callingUid,
                callingPackage, r);

        String bindFromPackage = null;
        if (ret == REASON_DENIED) {
            // If the callingUid is not allowed to start FGS, check if the callingUid has any
            // service that is bound by a clientUid, the clientUid can propagate its BG-FGS-start
            // capability down to the callingUid.
            final ArraySet<Integer> checkedClientUids = new ArraySet<>();
            final Pair<Integer, String> isAllowed = mAm.mProcessList.searchEachLruProcessesLOSP(
                    false, pr -> {
                if (pr.uid == callingUid) {
                    final ProcessServiceRecord psr = pr.mServices;
                    final int serviceCount = psr.mServices.size();
                    for (int svc = 0; svc < serviceCount; svc++) {
                        final ArrayMap<IBinder, ArrayList<ConnectionRecord>> conns =
                                psr.mServices.valueAt(svc).getConnections();
                        final int size = conns.size();
                        for (int conni = 0; conni < size; conni++) {
                            final ArrayList<ConnectionRecord> crs = conns.valueAt(conni);
                            for (int con = 0; con < crs.size(); con++) {
                                final ConnectionRecord cr = crs.get(con);
                                final ProcessRecord clientPr = cr.binding.client;
                                // Persistent process does not propagate BG-FGS-start capability
                                // down to service over binding.
                                if (clientPr.mState.getCurProcState()
                                        <= PROCESS_STATE_PERSISTENT_UI) {
                                    continue;
                                }
                                final int clientPid = clientPr.mPid;
                                final int clientUid = clientPr.uid;
                                // An UID can bind to itself, do not check on itself again.
                                // Also skip already checked clientUid.
                                if (clientUid == callingUid
                                        || checkedClientUids.contains(clientUid)) {
                                    continue;
                                }
                                final String clientPackageName = cr.clientPackageName;
                                final @ReasonCode int allowWhileInUse2 =
                                        shouldAllowFgsWhileInUsePermissionLocked(clientPackageName,
                                                clientPid, clientUid, null /* serviceRecord */,
                                                false /* allowBackgroundActivityStarts */);
                                final @ReasonCode int allowStartFgs =
                                        shouldAllowFgsStartForegroundLocked(allowWhileInUse2,
                                                clientPid, clientUid, clientPackageName, null /* targetService */);
                                if (allowStartFgs != REASON_DENIED) {
                                    return new Pair<>(allowStartFgs, clientPackageName);
                                } else {
                                    checkedClientUids.add(clientUid);
                                }

                            }
                        }
                    }
                }
                return null;
            });
            if (isAllowed != null) {
                ret = REASON_FGS_BINDING;
                bindFromPackage = isAllowed.second;
            }
        }

        final int uidState = mAm.getUidStateLocked(callingUid);
        int callerTargetSdkVersion = INVALID_UID;
        try {
@@ -5765,6 +5828,7 @@ public final class ActiveServices {
                        + "; targetSdkVersion:" + r.appInfo.targetSdkVersion
                        + "; callerTargetSdkVersion:" + callerTargetSdkVersion
                        + "; startForegroundCount:" + r.mStartForegroundCount
                        + "; bindFromPackage:" + bindFromPackage
                        + "]";
        if (!debugInfo.equals(r.mInfoAllowStartForeground)) {
            r.mLoggedInfoAllowStartForeground = false;
@@ -5790,9 +5854,7 @@ public final class ActiveServices {
            final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, app -> {
                if (app.uid == callingUid) {
                    final ProcessStateRecord state = app.mState;
                    if (state.getAllowedStartFgs() != REASON_DENIED) {
                        return state.getAllowedStartFgs();
                    } else if (state.isAllowedStartFgsState()) {
                    if (state.isAllowedStartFgsState()) {
                        return getReasonCodeFromProcState(state.getAllowStartFgsState());
                    } else if (state.areBackgroundFgsStartsAllowedByToken()) {
                        return REASON_FGS_BINDING;
@@ -5891,6 +5953,7 @@ public final class ActiveServices {
        }
        if (ret == REASON_DENIED) {
            if (mAm.mConstants.mFgsAllowOptOut
                    && targetService != null
                    && targetService.appInfo.hasRequestForegroundServiceExemption()) {
                ret = REASON_OPT_OUT_REQUESTED;
            }
+1 −11
Original line number Diff line number Diff line
@@ -42,7 +42,6 @@ import static android.app.ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE;
import static android.os.PowerWhitelistManager.REASON_DENIED;
import static android.os.Process.SCHED_OTHER;
import static android.os.Process.THREAD_GROUP_BACKGROUND;
import static android.os.Process.THREAD_GROUP_DEFAULT;
@@ -1574,8 +1573,7 @@ public class OomAdjuster {
        state.setAdjTarget(null);
        state.setEmpty(false);
        state.setCached(false);
        state.setAllowStartFgsState(PROCESS_STATE_NONEXISTENT);
        state.resetAllowStartFgs();
        state.resetAllowStartFgsState();
        app.mOptRecord.setShouldNotFreeze(false);

        final int appUid = app.info.uid;
@@ -1630,7 +1628,6 @@ public class OomAdjuster {
            state.setCurAdj(state.getMaxAdj());
            state.setCompletedAdjSeq(state.getAdjSeq());
            state.bumpAllowStartFgsState(state.getCurProcState());
            state.setAllowStartFgs();
            // if curAdj is less than prevAppAdj, then this process was promoted
            return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState;
        }
@@ -2028,12 +2025,6 @@ public class OomAdjuster {

                    final boolean clientIsSystem = clientProcState < PROCESS_STATE_TOP;

                    // pass client's mAllowStartFgs to the app if client is not persistent process.
                    if (cstate.getAllowedStartFgs() != REASON_DENIED
                            && cstate.getMaxAdj() >= ProcessList.FOREGROUND_APP_ADJ) {
                        state.setAllowStartFgs(cstate.getAllowedStartFgs());
                    }

                    if ((cr.flags & Context.BIND_WAIVE_PRIORITY) == 0) {
                        if (shouldSkipDueToCycle(state, cstate, procState, adj, cycleReEval)) {
                            continue;
@@ -2524,7 +2515,6 @@ public class OomAdjuster {
        state.updateLastInvisibleTime(hasVisibleActivities);
        state.setHasForegroundActivities(foregroundActivities);
        state.setCompletedAdjSeq(mAdjSeq);
        state.setAllowStartFgs();

        // if curAdj or curProcState improved, then this process was promoted
        return state.getCurAdj() < prevAppAdj || state.getCurProcState() < prevProcState
+1 −1
Original line number Diff line number Diff line
@@ -89,7 +89,7 @@ final class ProcessServiceRecord {
    /**
     * All ServiceRecord running in this process.
     */
    private final ArraySet<ServiceRecord> mServices = new ArraySet<>();
    final ArraySet<ServiceRecord> mServices = new ArraySet<>();

    /**
     * Services that are currently executing code (need to remain foreground).
+1 −139
Original line number Diff line number Diff line
@@ -16,28 +16,9 @@

package com.android.server.am;

import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.PowerWhitelistManager.REASON_BACKGROUND_ACTIVITY_PERMISSION;
import static android.os.PowerWhitelistManager.REASON_BACKGROUND_FGS_PERMISSION;
import static android.os.PowerWhitelistManager.REASON_DENIED;
import static android.os.PowerWhitelistManager.REASON_DEVICE_OWNER;
import static android.os.PowerWhitelistManager.REASON_PROFILE_OWNER;
import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
import static android.os.PowerWhitelistManager.REASON_SYSTEM_ALLOW_LISTED;
import static android.os.PowerWhitelistManager.REASON_SYSTEM_UID;
import static android.os.PowerWhitelistManager.ReasonCode;
import static android.os.PowerWhitelistManager.getReasonCodeFromProcState;
import static android.os.PowerWhitelistManager.reasonCodeToString;
import static android.os.Process.NFC_UID;
import static android.os.Process.ROOT_UID;
import static android.os.Process.SHELL_UID;
import static android.os.Process.SYSTEM_UID;

import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ProcessRecord.TAG;
@@ -47,7 +28,6 @@ import android.app.ActivityManager;
import android.content.ComponentName;
import android.os.Binder;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.Slog;
import android.util.TimeUtils;
@@ -56,7 +36,6 @@ import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.FrameworkStatsLog;


import java.io.PrintWriter;

/**
@@ -326,20 +305,6 @@ final class ProcessStateRecord {
    @GuardedBy("mService")
    private final ArraySet<Binder> mBackgroundFgsStartTokens = new ArraySet<>();

    /**
     * Does the process has permission to start FGS from background.
     */
    @GuardedBy("mService")
    private @ReasonCode int mAllowStartFgsByPermission = REASON_DENIED;

    /**
     * Can this process start FGS from background?
     * If this process has the ability to start FGS from background, this ability can be passed to
     * another process through service binding.
     */
    @GuardedBy("mService")
    private @ReasonCode int mAllowStartFgs = REASON_DENIED;

    /**
     * Whether or not this process has been in forced-app-standby state.
     */
@@ -435,7 +400,6 @@ final class ProcessStateRecord {
        mApp = app;
        mService = app.mService;
        mProcLock = mService.mProcLock;
        setAllowStartFgsByPermission();
    }

    void init(long now) {
@@ -1152,9 +1116,8 @@ final class ProcessStateRecord {
    }

    @GuardedBy("mService")
    void resetAllowStartFgs() {
    void resetAllowStartFgsState() {
        mAllowStartFgsState = PROCESS_STATE_NONEXISTENT;
        mAllowStartFgs = mAllowStartFgsByPermission;
    }

    @GuardedBy("mService")
@@ -1164,11 +1127,6 @@ final class ProcessStateRecord {
        }
    }

    @GuardedBy("mService")
    void setAllowStartFgsState(int allowStartFgsState) {
        mAllowStartFgsState = allowStartFgsState;
    }

    @GuardedBy("mService")
    int getAllowStartFgsState() {
        return mAllowStartFgsState;
@@ -1179,98 +1137,6 @@ final class ProcessStateRecord {
        return mAllowStartFgsState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
    }

    @GuardedBy("mService")
    void setAllowStartFgsByPermission() {
        int ret = REASON_DENIED;
        boolean isSystem = false;
        final int uid = UserHandle.getAppId(mApp.info.uid);
        switch (uid) {
            case ROOT_UID:
            case SYSTEM_UID:
            case NFC_UID:
            case SHELL_UID:
                isSystem = true;
                break;
            default:
                isSystem = false;
                break;
        }

        if (isSystem) {
            ret = REASON_SYSTEM_UID;
        }

        if (ret == REASON_DENIED) {
            if (ActivityManager.checkComponentPermission(START_ACTIVITIES_FROM_BACKGROUND,
                    mApp.info.uid, -1, true) == PERMISSION_GRANTED) {
                ret = REASON_BACKGROUND_ACTIVITY_PERMISSION;
            } else if (ActivityManager.checkComponentPermission(
                    START_FOREGROUND_SERVICES_FROM_BACKGROUND,
                    mApp.info.uid, -1, true) == PERMISSION_GRANTED) {
                ret = REASON_BACKGROUND_FGS_PERMISSION;
            } else if (ActivityManager.checkComponentPermission(SYSTEM_ALERT_WINDOW,
                    mApp.info.uid, -1, true) == PERMISSION_GRANTED) {
                ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
            }
        }
        mAllowStartFgs = mAllowStartFgsByPermission = ret;
    }

    // TODO(b/188063200) Clean up this method. Why do we need to duplicate only some of the checks?
    @GuardedBy("mService")
    void setAllowStartFgs() {
        if (mAllowStartFgs != REASON_DENIED) {
            return;
        }
        if (mAllowStartFgs == REASON_DENIED) {
            if (isAllowedStartFgsState()) {
                mAllowStartFgs = getReasonCodeFromProcState(mAllowStartFgsState);
            }
        }

        if (mAllowStartFgs == REASON_DENIED) {
            // Is the calling UID a device owner app?
            if (mService.mInternal != null) {
                if (mService.mInternal.isDeviceOwner(mApp.info.uid)) {
                    mAllowStartFgs = REASON_DEVICE_OWNER;
                }
            }
        }

        if (mAllowStartFgs == REASON_DENIED) {
            // Is the calling UID a profile owner app?
            if (mService.mInternal != null) {
                if (mService.mInternal.isProfileOwner(mApp.info.uid)) {
                    mAllowStartFgs = REASON_PROFILE_OWNER;
                }
            }
        }

        if (mAllowStartFgs == REASON_DENIED) {
            // uid is on DeviceIdleController's user/system allowlist
            // or AMS's FgsStartTempAllowList.
            ActivityManagerService.FgsTempAllowListItem item =
                    mService.isAllowlistedForFgsStartLOSP(mApp.info.uid);
            if (item != null) {
                if (item == ActivityManagerService.FAKE_TEMP_ALLOW_LIST_ITEM) {
                    mAllowStartFgs = REASON_SYSTEM_ALLOW_LISTED;
                } else {
                    mAllowStartFgs = item.mReasonCode;
                }
            }
        }
    }

    @GuardedBy("mService")
    void setAllowStartFgs(@ReasonCode int allowStartFgs) {
        mAllowStartFgs = allowStartFgs;
    }

    @GuardedBy("mService")
    @ReasonCode int getAllowedStartFgs() {
        return mAllowStartFgs;
    }

    @GuardedBy("mService")
    void setForcedAppStandby(boolean standby) {
        mForcedAppStandby = standby;
@@ -1334,10 +1200,6 @@ final class ProcessStateRecord {
        pw.println();
        pw.print(prefix); pw.print("allowStartFgsState=");
        pw.println(mAllowStartFgsState);
        if (mAllowStartFgs != REASON_DENIED) {
            pw.print(prefix); pw.print("allowStartFgs=");
            pw.println(reasonCodeToString(mAllowStartFgs));
        }
        if (mHasShownUi || mApp.mProfile.hasPendingUiClean()) {
            pw.print(prefix); pw.print("hasShownUi="); pw.print(mHasShownUi);
            pw.print(" pendingUiClean="); pw.println(mApp.mProfile.hasPendingUiClean());