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

Commit c0b56a39 authored by Kweku Adams's avatar Kweku Adams
Browse files

Allow UI-job scheduling from TOP+BAL-started FGS.

Allow apps to schedule user-initiated jobs from foreground services
started while the app was TOP or in a BAL-approved state (ie. when the
FGS started while the app was in a state when it could schedule a UI
job).

Bug: 261999509
Bug: 271846333
Test: atest CtsJobSchedulerTestCases:UserInitiatedJobTest
Change-Id: I1c87d4254fa0f3349c6f2af349295582f6fe8427
parent 0b5e5e38
Loading
Loading
Loading
Loading
+22 −1
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ import static android.os.PowerExemptionManager.REASON_SYSTEM_MODULE;
import static android.os.PowerExemptionManager.REASON_SYSTEM_UID;
import static android.os.PowerExemptionManager.REASON_TEMP_ALLOWED_WHILE_IN_USE;
import static android.os.PowerExemptionManager.REASON_UID_VISIBLE;
import static android.os.PowerExemptionManager.REASON_UNKNOWN;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.PowerExemptionManager.getReasonCodeFromProcState;
import static android.os.PowerExemptionManager.reasonCodeToString;
@@ -7319,9 +7320,10 @@ public final class ActiveServices {
            r.mAllowWhileInUsePermissionInFgs = true;
        }

        final @ReasonCode int allowWhileInUse;
        if (!r.mAllowWhileInUsePermissionInFgs
                || (r.mAllowStartForeground == REASON_DENIED)) {
            final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
            allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
                    callingPackage, callingPid, callingUid, r.app, backgroundStartPrivileges,
                    isBindService);
            if (!r.mAllowWhileInUsePermissionInFgs) {
@@ -7332,6 +7334,24 @@ public final class ActiveServices {
                        allowWhileInUse, callingPackage, callingPid, callingUid, intent, r,
                        backgroundStartPrivileges, isBindService);
            }
        } else {
            allowWhileInUse = REASON_UNKNOWN;
        }
        // We want to allow scheduling user-initiated jobs when the app is running a
        // foreground service that was started in the same conditions that allows for scheduling
        // UI jobs. More explicitly, we want to allow scheduling UI jobs when the app is running
        // an FGS that started when the app was in the TOP or a BAL-approved state.
        // As of Android UDC, the conditions required for the while-in-use permissions
        // are the same conditions that we want, so we piggyback on that logic.
        // We use that as a shortcut if possible so we don't have to recheck all the conditions.
        final boolean isFgs = r.isForeground || r.fgRequired;
        if (isFgs) {
            r.updateAllowUiJobScheduling(ActivityManagerService
                    .doesReasonCodeAllowSchedulingUserInitiatedJobs(allowWhileInUse)
                    || mAm.canScheduleUserInitiatedJobs(
                            callingUid, callingPid, callingPackage, true));
        } else {
            r.updateAllowUiJobScheduling(false);
        }
    }

@@ -7342,6 +7362,7 @@ public final class ActiveServices {
        r.mInfoTempFgsAllowListReason = null;
        r.mLoggedInfoAllowStartForeground = false;
        r.mLastSetFgsRestrictionTime = 0;
        r.updateAllowUiJobScheduling(r.mAllowWhileInUsePermissionInFgs);
    }

    boolean canStartForegroundServiceLocked(int callingPid, int callingUid, String callingPackage) {
+17 −2
Original line number Diff line number Diff line
@@ -6563,7 +6563,7 @@ public class ActivityManagerService extends IActivityManager.Stub
     * This is a shortcut and <b>DOES NOT</b> include all reasons.
     * Use {@link #canScheduleUserInitiatedJobs(int, int, String)} to cover all cases.
     */
    private boolean doesReasonCodeAllowSchedulingUserInitiatedJobs(int reasonCode) {
    static boolean doesReasonCodeAllowSchedulingUserInitiatedJobs(int reasonCode) {
        switch (reasonCode) {
            case REASON_PROC_STATE_PERSISTENT:
            case REASON_PROC_STATE_PERSISTENT_UI:
@@ -6621,6 +6621,16 @@ public class ActivityManagerService extends IActivityManager.Stub
            }
        }
        final ProcessServiceRecord psr = pr.mServices;
        if (psr != null && psr.hasForegroundServices()) {
            for (int s = psr.numberOfExecutingServices() - 1; s >= 0; --s) {
                final ServiceRecord sr = psr.getExecutingServiceAt(s);
                if (sr.isForeground && sr.mAllowUiJobScheduling) {
                    return true;
                }
            }
        }
        return false;
    }
@@ -6630,6 +6640,11 @@ public class ActivityManagerService extends IActivityManager.Stub
     */
    // TODO(262260570): log allow reason to an atom
    private boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName) {
        return canScheduleUserInitiatedJobs(uid, pid, pkgName, false);
    }
    boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName,
            boolean skipWhileInUseCheck) {
        synchronized (this) {
            final ProcessRecord processRecord;
            synchronized (mPidsSelfLocked) {
@@ -6659,7 +6674,7 @@ public class ActivityManagerService extends IActivityManager.Stub
            // As of Android UDC, the conditions required to grant a while-in-use permission
            // covers the majority of those cases, and so we piggyback on that logic as the base.
            // Missing cases are added after.
            if (mServices.canAllowWhileInUsePermissionInFgsLocked(
            if (!skipWhileInUseCheck && mServices.canAllowWhileInUsePermissionInFgsLocked(
                    pid, uid, pkgName, processRecord, backgroundStartPrivileges)) {
                return true;
            }
+13 −0
Original line number Diff line number Diff line
@@ -176,6 +176,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
    boolean mAllowWhileInUsePermissionInFgs;
    // A copy of mAllowWhileInUsePermissionInFgs's value when the service is entering FGS state.
    boolean mAllowWhileInUsePermissionInFgsAtEntering;
    /** Allow scheduling user-initiated jobs from the background. */
    boolean mAllowUiJobScheduling;

    // the most recent package that start/bind this service.
    String mRecentCallingPackage;
@@ -607,6 +609,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
        }
        pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
                pw.println(mAllowWhileInUsePermissionInFgs);
        pw.print(prefix); pw.print("allowUiJobScheduling="); pw.println(mAllowUiJobScheduling);
        pw.print(prefix); pw.print("recentCallingPackage=");
                pw.println(mRecentCallingPackage);
        pw.print(prefix); pw.print("recentCallingUid=");
@@ -1024,7 +1027,17 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN
                ams.mConstants.SERVICE_BG_ACTIVITY_START_TIMEOUT);
    }

    void updateAllowUiJobScheduling(boolean allowUiJobScheduling) {
        if (mAllowUiJobScheduling == allowUiJobScheduling) {
            return;
        }
        mAllowUiJobScheduling = allowUiJobScheduling;
    }

    private void setAllowedBgActivityStartsByStart(BackgroundStartPrivileges newValue) {
        if (mBackgroundStartPrivilegesByStartMerged == newValue) {
            return;
        }
        mBackgroundStartPrivilegesByStartMerged = newValue;
        updateParentProcessBgActivityStartsToken();
    }