diff options
| author | 2023-03-21 14:48:57 +0000 | |
|---|---|---|
| committer | 2023-03-21 14:48:57 +0000 | |
| commit | e87bf3f9dede21e14873efedc48920b00468007f (patch) | |
| tree | 154096e609baefa608045bb6a3a52e93cb6d317f | |
| parent | 2bc270acca73cd6b6b0db20cc9e58b2d7bc93d93 (diff) | |
| parent | d9a29ede5c2af95a3d9eed9af9818fecc16e2f87 (diff) | |
Merge "Add additional UI job scheduling cases." into udc-dev
7 files changed, 255 insertions, 34 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java index 89eb1a9a7650..4477e94b77f1 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java @@ -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; diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index 08810b549e62..4cf9c8cfb6fe 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -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,25 +3839,17 @@ 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"); - return JobScheduler.RESULT_FAILURE; - } + 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) { @@ -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; } diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index b96f8c94aaa3..0293bb53d3f0 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -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(); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index c129938ea7b1..461103ee4355 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -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 callingUid caller app's uid. + * @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 diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 4fa28a11c0a4..82c4796a21ef 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -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% @@ -680,6 +684,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, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9e5acf796025..415c859f734d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -23,6 +23,7 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS; import static android.Manifest.permission.MANAGE_USERS; +import static android.Manifest.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND; import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND; import static android.app.ActivityManager.INSTR_FLAG_ALWAYS_CHECK_SIGNATURE; @@ -32,6 +33,7 @@ import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS; import static android.app.ActivityManager.INSTR_FLAG_NO_RESTART; import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY; import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL; +import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.ActivityManager.PROCESS_STATE_TOP; @@ -60,9 +62,22 @@ import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; 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_ACTIVITY_VISIBILITY_GRACE_PERIOD; +import static android.os.PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION; +import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER; +import static android.os.PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION; +import static android.os.PowerExemptionManager.REASON_PROC_STATE_BTOP; +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_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_UID_VISIBLE; 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.PowerExemptionManager.getReasonCodeFromProcState; import static android.os.Process.BLUETOOTH_UID; import static android.os.Process.FIRST_APPLICATION_UID; import static android.os.Process.INVALID_UID; @@ -6541,6 +6556,143 @@ public class ActivityManagerService extends IActivityManager.Stub } /** + * Returns true if the reasonCode is included in the base set of reasons an app may be + * allowed to schedule a + * {@link android.app.job.JobInfo.Builder#setUserInitiated(boolean) user-initiated job}. + * 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) { + switch (reasonCode) { + case REASON_PROC_STATE_PERSISTENT: + case REASON_PROC_STATE_PERSISTENT_UI: + case REASON_PROC_STATE_TOP: + case REASON_PROC_STATE_BTOP: + case REASON_UID_VISIBLE: + case REASON_SYSTEM_UID: + case REASON_START_ACTIVITY_FLAG: + case REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD: + case REASON_SYSTEM_ALERT_WINDOW_PERMISSION: + case REASON_COMPANION_DEVICE_MANAGER: + case REASON_BACKGROUND_ACTIVITY_PERMISSION: + case REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION: + return true; + } + return false; + } + + /** + * Returns true if the ProcessRecord has some conditions that allow the app to schedule a + * {@link android.app.job.JobInfo.Builder#setUserInitiated(boolean) user-initiated job}. + * This is a shortcut and <b>DOES NOT</b> include all reasons. + * Use {@link #canScheduleUserInitiatedJobs(int, int, String)} to cover all cases. + */ + @GuardedBy(anyOf = {"this", "mProcLock"}) + private boolean isProcessInStateToScheduleUserInitiatedJobsLocked( + @Nullable ProcessRecord pr, long nowElapsed) { + if (pr == null) { + return false; + } + + final BackgroundStartPrivileges backgroundStartPrivileges = + pr.getBackgroundStartPrivileges(); + // Is the allow activity background start flag on? + if (backgroundStartPrivileges.allowsBackgroundActivityStarts()) { + // REASON_START_ACTIVITY_FLAG; + return true; + } + + final ProcessStateRecord state = pr.mState; + final int procstate = state.getCurProcState(); + if (procstate <= PROCESS_STATE_BOUND_TOP) { + if (doesReasonCodeAllowSchedulingUserInitiatedJobs( + getReasonCodeFromProcState(procstate))) { + return true; + } + } + + final long lastInvisibleTime = state.getLastInvisibleTime(); + if (lastInvisibleTime > 0 && lastInvisibleTime < Long.MAX_VALUE) { + final long timeSinceVisibleMs = nowElapsed - lastInvisibleTime; + if (timeSinceVisibleMs < mConstants.mVisibleToInvisibleUijScheduleGraceDurationMs) { + // REASON_ACTIVITY_VISIBILITY_GRACE_PERIOD + return true; + } + } + + return false; + } + + /** + * Returns whether the app in question is in a state where we allow scheduling a + * {@link android.app.job.JobInfo.Builder#setUserInitiated(boolean) user-initiated job}. + */ + // TODO(262260570): log allow reason to an atom + private boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName) { + synchronized (this) { + final ProcessRecord processRecord; + synchronized (mPidsSelfLocked) { + processRecord = mPidsSelfLocked.get(pid); + } + + final long nowElapsed = SystemClock.elapsedRealtime(); + final BackgroundStartPrivileges backgroundStartPrivileges; + if (processRecord != null) { + if (isProcessInStateToScheduleUserInitiatedJobsLocked(processRecord, nowElapsed)) { + return true; + } + backgroundStartPrivileges = processRecord.getBackgroundStartPrivileges(); + } else { + backgroundStartPrivileges = getBackgroundStartPrivileges(uid); + } + // Is the allow activity background start flag on? + if (backgroundStartPrivileges.allowsBackgroundActivityStarts()) { + // REASON_START_ACTIVITY_FLAG; + return true; + } + + // We allow scheduling a user-initiated job when the app is in the TOP or a + // Background Activity Launch approved state. These are cases that indicate the user + // has interacted with the app and therefore it is reasonable to believe the app may + // attempt to schedule a user-initiated job in response to the user interaction. + // 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( + pid, uid, pkgName, processRecord, backgroundStartPrivileges)) { + return true; + } + + final UidRecord uidRecord = mProcessList.getUidRecordLOSP(uid); + if (uidRecord != null) { + for (int i = uidRecord.getNumOfProcs() - 1; i >= 0; --i) { + ProcessRecord pr = uidRecord.getProcessRecordByIndex(i); + if (isProcessInStateToScheduleUserInitiatedJobsLocked(pr, nowElapsed)) { + return true; + } + } + } + + if (mAtmInternal.hasSystemAlertWindowPermission(uid, pid, pkgName)) { + // REASON_SYSTEM_ALERT_WINDOW_PERMISSION; + return true; + } + + final int userId = UserHandle.getUserId(uid); + final boolean isCompanionApp = mInternal.isAssociatedCompanionApp(userId, uid); + if (isCompanionApp) { + if (checkPermission(REQUEST_COMPANION_RUN_IN_BACKGROUND, pid, uid) + == PERMISSION_GRANTED) { + // REASON_COMPANION_DEVICE_MANAGER; + return true; + } + } + } + + return false; + } + + /** * @return allowlist tag for a uid from mPendingTempAllowlist, null if not currently on * the allowlist */ @@ -18156,6 +18308,11 @@ public class ActivityManagerService extends IActivityManager.Stub return ActivityManagerService.this.getBackgroundStartPrivileges(uid); } + @Override + public boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName) { + return ActivityManagerService.this.canScheduleUserInitiatedJobs(uid, pid, pkgName); + } + public void reportCurKeyguardUsageEvent(boolean keyguardShowing) { ActivityManagerService.this.reportGlobalUsageEvent(keyguardShowing ? UsageEvents.Event.KEYGUARD_SHOWN diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java index e38e8c1428ee..e39ac2b08479 100644 --- a/services/core/java/com/android/server/am/UidRecord.java +++ b/services/core/java/com/android/server/am/UidRecord.java @@ -311,6 +311,11 @@ public final class UidRecord { } @GuardedBy(anyOf = {"mService", "mProcLock"}) + ProcessRecord getProcessRecordByIndex(int idx) { + return mProcRecords.valueAt(idx); + } + + @GuardedBy(anyOf = {"mService", "mProcLock"}) ProcessRecord getProcessInPackage(String packageName) { for (int i = mProcRecords.size() - 1; i >= 0; i--) { final ProcessRecord app = mProcRecords.valueAt(i); |