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

Commit e87bf3f9 authored by Kweku Adams's avatar Kweku Adams Committed by Android (Google) Code Review
Browse files

Merge "Add additional UI job scheduling cases." into udc-dev

parents 2bc270ac d9a29ede
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -1259,10 +1259,14 @@ class JobConcurrencyManager {

                final BackgroundStartPrivileges bsp =
                        activityManagerInternal.getBackgroundStartPrivileges(uid);
                final boolean balAllowed = bsp.allowsBackgroundActivityStarts();
                if (DEBUG) {
                    Slog.d(TAG, "Job " + job.toShortString() + " bal state: " + bsp);
                    Slog.d(TAG, "Job " + job.toShortString() + " bsp state: " + bsp);
                }
                // Intentionally use the background activity start BSP here instead of
                // the full BAL check since the former is transient and better indicates that the
                // user recently interacted with the app, while the latter includes
                // permanent exceptions that don't warrant bypassing normal concurrency policy.
                final boolean balAllowed = bsp.allowsBackgroundActivityStarts();
                cachedPrivilegedState.put(uid,
                        balAllowed ? PRIVILEGED_STATE_BAL : PRIVILEGED_STATE_NONE);
                return balAllowed;
+41 −23
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.BackgroundStartPrivileges;
import android.app.IUidObserver;
import android.app.compat.CompatChanges;
import android.app.job.IJobScheduler;
@@ -3782,7 +3781,8 @@ public class JobSchedulerService extends com.android.server.SystemService
            return canPersist;
        }

        private int validateJob(@NonNull JobInfo job, int callingUid, int sourceUserId,
        private int validateJob(@NonNull JobInfo job, int callingUid, int callingPid,
                int sourceUserId,
                @Nullable String sourcePkgName, @Nullable JobWorkItem jobWorkItem) {
            final boolean rejectNegativeNetworkEstimates = CompatChanges.isChangeEnabled(
                            JobInfo.REJECT_NEGATIVE_NETWORK_ESTIMATES, callingUid);
@@ -3815,6 +3815,8 @@ public class JobSchedulerService extends com.android.server.SystemService
                }
                // We aim to check the permission of both the source and calling app so that apps
                // don't attempt to bypass the permission by using other apps to do the work.
                boolean isInStateToScheduleUiJobSource = false;
                final String callingPkgName = job.getService().getPackageName();
                if (sourceUid != -1) {
                    // Check the permission of the source app.
                    final int sourceResult =
@@ -3822,8 +3824,13 @@ public class JobSchedulerService extends com.android.server.SystemService
                    if (sourceResult != JobScheduler.RESULT_SUCCESS) {
                        return sourceResult;
                    }
                    final int sourcePid =
                            callingUid == sourceUid && callingPkgName.equals(sourcePkgName)
                                    ? callingPid : -1;
                    isInStateToScheduleUiJobSource = isInStateToScheduleUserInitiatedJobs(
                            sourceUid, sourcePid, sourcePkgName);
                }
                final String callingPkgName = job.getService().getPackageName();
                boolean isInStateToScheduleUiJobCalling = false;
                if (callingUid != sourceUid || !callingPkgName.equals(sourcePkgName)) {
                    // Source app is different from calling app. Make sure the calling app also has
                    // the permission.
@@ -3832,27 +3839,19 @@ public class JobSchedulerService extends com.android.server.SystemService
                    if (callingResult != JobScheduler.RESULT_SUCCESS) {
                        return callingResult;
                    }
                    // Avoid rechecking the state if the source app is able to schedule the job.
                    if (!isInStateToScheduleUiJobSource) {
                        isInStateToScheduleUiJobCalling = isInStateToScheduleUserInitiatedJobs(
                                callingUid, callingPid, callingPkgName);
                    }

                final int uid = sourceUid != -1 ? sourceUid : callingUid;
                final int procState = mActivityManagerInternal.getUidProcessState(uid);
                if (DEBUG) {
                    Slog.d(TAG, "Uid " + uid + " proc state="
                            + ActivityManager.procStateToString(procState));
                }
                if (procState != ActivityManager.PROCESS_STATE_TOP) {
                    final BackgroundStartPrivileges bsp =
                            mActivityManagerInternal.getBackgroundStartPrivileges(uid);
                    if (DEBUG) {
                        Slog.d(TAG, "Uid " + uid + ": " + bsp);
                }
                    if (!bsp.allowsBackgroundActivityStarts()) {
                        Slog.e(TAG,
                                "Uid " + uid + " not in a state to schedule user-initiated jobs");

                if (!isInStateToScheduleUiJobSource && !isInStateToScheduleUiJobCalling) {
                    Slog.e(TAG, "Uid(s) " + sourceUid + "/" + callingUid
                            + " not in a state to schedule user-initiated jobs");
                    return JobScheduler.RESULT_FAILURE;
                }
            }
            }
            if (jobWorkItem != null) {
                jobWorkItem.enforceValidity(rejectNegativeNetworkEstimates);
                if (jobWorkItem.getEstimatedNetworkDownloadBytes() != JobInfo.NETWORK_BYTES_UNKNOWN
@@ -3896,6 +3895,24 @@ public class JobSchedulerService extends com.android.server.SystemService
            return JobScheduler.RESULT_SUCCESS;
        }

        private boolean isInStateToScheduleUserInitiatedJobs(int uid, int pid, String pkgName) {
            final int procState = mActivityManagerInternal.getUidProcessState(uid);
            if (DEBUG) {
                Slog.d(TAG, "Uid " + uid + " proc state="
                        + ActivityManager.procStateToString(procState));
            }
            if (procState == ActivityManager.PROCESS_STATE_TOP) {
                return true;
            }
            final boolean canScheduleUiJobsInBg =
                    mActivityManagerInternal.canScheduleUserInitiatedJobs(uid, pid, pkgName);
            if (DEBUG) {
                Slog.d(TAG, "Uid " + uid
                        + " AM.canScheduleUserInitiatedJobs= " + canScheduleUiJobsInBg);
            }
            return canScheduleUiJobsInBg;
        }

        // IJobScheduler implementation
        @Override
        public int schedule(String namespace, JobInfo job) throws RemoteException {
@@ -3908,7 +3925,7 @@ public class JobSchedulerService extends com.android.server.SystemService

            enforceValidJobRequest(uid, pid, job);

            final int result = validateJob(job, uid, -1, null, null);
            final int result = validateJob(job, uid, pid, -1, null, null);
            if (result != JobScheduler.RESULT_SUCCESS) {
                return result;
            }
@@ -3941,7 +3958,7 @@ public class JobSchedulerService extends com.android.server.SystemService
                throw new NullPointerException("work is null");
            }

            final int result = validateJob(job, uid, -1, null, work);
            final int result = validateJob(job, uid, pid, -1, null, work);
            if (result != JobScheduler.RESULT_SUCCESS) {
                return result;
            }
@@ -3963,6 +3980,7 @@ public class JobSchedulerService extends com.android.server.SystemService
        public int scheduleAsPackage(String namespace, JobInfo job, String packageName, int userId,
                String tag) throws RemoteException {
            final int callerUid = Binder.getCallingUid();
            final int callerPid = Binder.getCallingPid();
            if (DEBUG) {
                Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
                        + " on behalf of " + packageName + "/");
@@ -3979,7 +3997,7 @@ public class JobSchedulerService extends com.android.server.SystemService
                        + " not permitted to schedule jobs for other apps");
            }

            int result = validateJob(job, callerUid, userId, packageName, null);
            int result = validateJob(job, callerUid, callerPid, userId, packageName, null);
            if (result != JobScheduler.RESULT_SUCCESS) {
                return result;
            }
+6 −0
Original line number Diff line number Diff line
@@ -474,6 +474,12 @@ public abstract class ActivityManagerInternal {
    public abstract BackgroundStartPrivileges getBackgroundStartPrivileges(int uid);
    public abstract void reportCurKeyguardUsageEvent(boolean keyguardShowing);

    /**
     * Returns whether the app is in a state where it is allowed to schedule a
     * {@link android.app.job.JobInfo.Builder#setUserInitiated(boolean) user-initiated job}.
     */
    public abstract boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName);

    /** @see com.android.server.am.ActivityManagerService#monitor */
    public abstract void monitor();

+17 −9
Original line number Diff line number Diff line
@@ -7322,7 +7322,7 @@ public final class ActiveServices {
        if (!r.mAllowWhileInUsePermissionInFgs
                || (r.mAllowStartForeground == REASON_DENIED)) {
            final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
                    callingPackage, callingPid, callingUid, r, backgroundStartPrivileges,
                    callingPackage, callingPid, callingUid, r.app, backgroundStartPrivileges,
                    isBindService);
            if (!r.mAllowWhileInUsePermissionInFgs) {
                r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != REASON_DENIED);
@@ -7349,7 +7349,7 @@ public final class ActiveServices {
            return true;
        }
        final @ReasonCode int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked(
                callingPackage, callingPid, callingUid, null /* serviceRecord */,
                callingPackage, callingPid, callingUid, null /* targetProcess */,
                BackgroundStartPrivileges.NONE, false);
        @ReasonCode int allowStartFgs = shouldAllowFgsStartForegroundNoBindingCheckLocked(
                allowWhileInUse, callingPid, callingUid, callingPackage, null /* targetService */,
@@ -7366,13 +7366,14 @@ public final class ActiveServices {
    /**
     * Should allow while-in-use permissions in FGS or not.
     * A typical BG started FGS is not allowed to have while-in-use permissions.
     *
     * @param callingPackage caller app's package name.
     * @param callingUid     caller app's uid.
     * @param targetService the service to start.
     * @param targetProcess  the process of the service to start.
     * @return {@link ReasonCode}
     */
    private @ReasonCode int shouldAllowFgsWhileInUsePermissionLocked(String callingPackage,
            int callingPid, int callingUid, @Nullable ServiceRecord targetService,
            int callingPid, int callingUid, @Nullable ProcessRecord targetProcess,
            BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) {
        int ret = REASON_DENIED;

@@ -7440,8 +7441,8 @@ public final class ActiveServices {
        }

        if (ret == REASON_DENIED) {
            if (targetService != null && targetService.app != null) {
                ActiveInstrumentation instr = targetService.app.getActiveInstrumentation();
            if (targetProcess != null) {
                ActiveInstrumentation instr = targetProcess.getActiveInstrumentation();
                if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
                    ret = REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
                }
@@ -7526,7 +7527,7 @@ public final class ActiveServices {
                                final @ReasonCode int allowWhileInUse2 =
                                        shouldAllowFgsWhileInUsePermissionLocked(
                                                clientPackageName,
                                                clientPid, clientUid, null /* serviceRecord */,
                                                clientPid, clientUid, null /* targetProcess */,
                                                BackgroundStartPrivileges.NONE, false);
                                final @ReasonCode int allowStartFgs =
                                        shouldAllowFgsStartForegroundNoBindingCheckLocked(
@@ -7946,11 +7947,18 @@ public final class ActiveServices {
    boolean canAllowWhileInUsePermissionInFgsLocked(int callingPid, int callingUid,
            String callingPackage) {
        return shouldAllowFgsWhileInUsePermissionLocked(callingPackage, callingPid, callingUid,
                /* targetService */ null,
                /* targetProcess */ null,
                BackgroundStartPrivileges.NONE, false)
                != REASON_DENIED;
    }

    boolean canAllowWhileInUsePermissionInFgsLocked(int callingPid, int callingUid,
            String callingPackage, @Nullable ProcessRecord targetProcess,
            @NonNull BackgroundStartPrivileges backgroundStartPrivileges) {
        return shouldAllowFgsWhileInUsePermissionLocked(callingPackage, callingPid, callingUid,
                targetProcess, backgroundStartPrivileges, false) != REASON_DENIED;
    }

    /**
     * Checks if a given packageName belongs to a given uid.
     * @param packageName the package of the caller
+23 −0
Original line number Diff line number Diff line
@@ -118,6 +118,8 @@ final class ActivityManagerConstants extends ContentObserver {
    static final String KEY_PROCESS_CRASH_COUNT_LIMIT = "process_crash_count_limit";
    static final String KEY_BOOT_TIME_TEMP_ALLOWLIST_DURATION = "boot_time_temp_allowlist_duration";
    static final String KEY_FG_TO_BG_FGS_GRACE_DURATION = "fg_to_bg_fgs_grace_duration";
    static final String KEY_VISIBLE_TO_INVISIBLE_UIJ_SCHEDULE_GRACE_DURATION =
            "vis_to_invis_uij_schedule_grace_duration";
    static final String KEY_FGS_START_FOREGROUND_TIMEOUT = "fgs_start_foreground_timeout";
    static final String KEY_FGS_ATOM_SAMPLE_RATE = "fgs_atom_sample_rate";
    static final String KEY_FGS_START_ALLOWED_LOG_SAMPLE_RATE = "fgs_start_allowed_log_sample_rate";
@@ -191,6 +193,8 @@ final class ActivityManagerConstants extends ContentObserver {
    private static final int DEFAULT_PROCESS_CRASH_COUNT_LIMIT = 12;
    private static final int DEFAULT_BOOT_TIME_TEMP_ALLOWLIST_DURATION = 20 * 1000;
    private static final long DEFAULT_FG_TO_BG_FGS_GRACE_DURATION = 5 * 1000;
    private static final long DEFAULT_VISIBLE_TO_INVISIBLE_UIJ_SCHEDULE_GRACE_DURATION =
            DEFAULT_FG_TO_BG_FGS_GRACE_DURATION;
    private static final int DEFAULT_FGS_START_FOREGROUND_TIMEOUT_MS = 10 * 1000;
    private static final float DEFAULT_FGS_ATOM_SAMPLE_RATE = 1; // 100 %
    private static final float DEFAULT_FGS_START_ALLOWED_LOG_SAMPLE_RATE = 0.25f; // 25%
@@ -679,6 +683,15 @@ final class ActivityManagerConstants extends ContentObserver {
     */
    volatile long mFgToBgFgsGraceDuration = DEFAULT_FG_TO_BG_FGS_GRACE_DURATION;

    /**
     * The grace period in milliseconds to allow a process to schedule a
     * {@link android.app.job.JobInfo.Builder#setUserInitiated(boolean) user-initiated job}
     * after switching from visible to a non-visible state.
     * Currently it's only applicable to its activities.
     */
    volatile long mVisibleToInvisibleUijScheduleGraceDurationMs =
            DEFAULT_VISIBLE_TO_INVISIBLE_UIJ_SCHEDULE_GRACE_DURATION;

    /**
     * When service started from background, before the timeout it can be promoted to FGS by calling
     * Service.startForeground().
@@ -1123,6 +1136,9 @@ final class ActivityManagerConstants extends ContentObserver {
                            case KEY_FG_TO_BG_FGS_GRACE_DURATION:
                                updateFgToBgFgsGraceDuration();
                                break;
                            case KEY_VISIBLE_TO_INVISIBLE_UIJ_SCHEDULE_GRACE_DURATION:
                                updateFgToBgFgsGraceDuration();
                                break;
                            case KEY_FGS_START_FOREGROUND_TIMEOUT:
                                updateFgsStartForegroundTimeout();
                                break;
@@ -1598,6 +1614,13 @@ final class ActivityManagerConstants extends ContentObserver {
                DEFAULT_FG_TO_BG_FGS_GRACE_DURATION);
    }

    private void updateVisibleToInvisibleUijScheduleGraceDuration() {
        mVisibleToInvisibleUijScheduleGraceDurationMs = DeviceConfig.getLong(
                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                KEY_VISIBLE_TO_INVISIBLE_UIJ_SCHEDULE_GRACE_DURATION,
                DEFAULT_VISIBLE_TO_INVISIBLE_UIJ_SCHEDULE_GRACE_DURATION);
    }

    private void updateFgsStartForegroundTimeout() {
        mFgsStartForegroundTimeoutMs = DeviceConfig.getLong(
                DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
Loading