From 6b3130423780176785071d4f766d4010026256b6 Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Tue, 11 Apr 2023 20:55:10 +0000 Subject: [PATCH] Fix allow-UIJ-scheduling determination. The ServiceRecord.fgRequired bit is updated after the call to check the FGS while-in-use permission. This meant that trying to set the allow-uij-scheduling bit in that method while limiting it to foreground services didn't work properly. Instead, wait until the system has confirmed the FGS can start and updated the ServiceRecord bits before setting the allow-uij-scheduling bit. Bug: 277635240 Test: atest CtsJobSchedulerTestCases:UserInitiatedJobTest Change-Id: Iccb8a9b2ee39745f8f9c9d5db4bcf969cf175997 --- .../com/android/server/am/ActiveServices.java | 36 ++++++++++--------- .../server/am/ActivityManagerService.java | 14 +++----- .../com/android/server/am/ServiceRecord.java | 4 +++ 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 7b618b11bd450..a982c19a8a1b8 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1007,6 +1007,24 @@ public final class ActiveServices { r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), service, neededGrants, callingUid, callingProcessName, callingPackage)); + // 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. + final boolean isFgs = r.isForeground || r.fgRequired; + if (isFgs) { + // 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. + // Use that as a shortcut if possible to avoid having to recheck all the conditions. + final boolean whileInUseAllowsUiJobScheduling = + ActivityManagerService.doesReasonCodeAllowSchedulingUserInitiatedJobs( + r.mAllowWhileInUsePermissionInFgsReason); + r.updateAllowUiJobScheduling(whileInUseAllowsUiJobScheduling + || mAm.canScheduleUserInitiatedJobs(callingUid, callingPid, callingPackage)); + } else { + r.updateAllowUiJobScheduling(false); + } + if (fgRequired) { // We are now effectively running a foreground service. synchronized (mAm.mProcessStats.mLock) { @@ -7344,26 +7362,12 @@ public final class ActiveServices { } 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); - } + r.mAllowWhileInUsePermissionInFgsReason = allowWhileInUse; } void resetFgsRestrictionLocked(ServiceRecord r) { r.mAllowWhileInUsePermissionInFgs = false; + r.mAllowWhileInUsePermissionInFgsReason = REASON_DENIED; r.mAllowStartForeground = REASON_DENIED; r.mInfoAllowStartForeground = null; r.mInfoTempFgsAllowListReason = null; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9752ade1ce348..3dc01a162b3c3 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -368,7 +368,6 @@ import android.util.FeatureFlagUtils; import android.util.IndentingPrintWriter; import android.util.IntArray; import android.util.Log; -import android.util.LogWriter; import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Slog; @@ -6181,8 +6180,8 @@ 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); + for (int s = psr.numberOfRunningServices() - 1; s >= 0; --s) { + final ServiceRecord sr = psr.getRunningServiceAt(s); if (sr.isForeground && sr.mAllowUiJobScheduling) { return true; } @@ -6197,12 +6196,7 @@ public class ActivityManagerService extends IActivityManager.Stub * {@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) { - return canScheduleUserInitiatedJobs(uid, pid, pkgName, false); - } - - boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName, - boolean skipWhileInUseCheck) { + boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName) { synchronized (this) { final ProcessRecord processRecord; synchronized (mPidsSelfLocked) { @@ -6232,7 +6226,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 (!skipWhileInUseCheck && mServices.canAllowWhileInUsePermissionInFgsLocked( + if (mServices.canAllowWhileInUsePermissionInFgsLocked( pid, uid, pkgName, processRecord, backgroundStartPrivileges)) { return true; } diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index edf0dbd65ef2c..a875860f016f2 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -174,6 +174,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // allow while-in-use permissions in foreground service or not. // while-in-use permissions in FGS started from background might be restricted. boolean mAllowWhileInUsePermissionInFgs; + @PowerExemptionManager.ReasonCode + int mAllowWhileInUsePermissionInFgsReason; // A copy of mAllowWhileInUsePermissionInFgs's value when the service is entering FGS state. boolean mAllowWhileInUsePermissionInFgsAtEntering; /** Allow scheduling user-initiated jobs from the background. */ @@ -609,6 +611,8 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs="); pw.println(mAllowWhileInUsePermissionInFgs); + pw.print(prefix); pw.print("mAllowWhileInUsePermissionInFgsReason="); + pw.println(mAllowWhileInUsePermissionInFgsReason); pw.print(prefix); pw.print("allowUiJobScheduling="); pw.println(mAllowUiJobScheduling); pw.print(prefix); pw.print("recentCallingPackage="); pw.println(mRecentCallingPackage);