Loading apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java +3 −0 Original line number Diff line number Diff line Loading @@ -200,7 +200,10 @@ class JobNotificationCoordinator { // No more jobs using this notification. Apply the final job stop policy. // If the user attempted to stop the job/app, then always remove the notification // so the user doesn't get confused about the app state. // Similarly, if the user background restricted the app, remove the notification so // the user doesn't think the app is continuing to run in the background. if (details.jobEndNotificationPolicy == JOB_END_NOTIFICATION_POLICY_REMOVE || stopReason == JobParameters.STOP_REASON_BACKGROUND_RESTRICTION || stopReason == JobParameters.STOP_REASON_USER) { mNotificationManagerInternal.cancelNotification( packageName, packageName, details.appUid, details.appPid, /* tag */ null, Loading apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +11 −2 Original line number Diff line number Diff line Loading @@ -413,16 +413,22 @@ public final class JobServiceContext implements ServiceConnection { final Intent intent = new Intent().setComponent(job.getServiceComponent()) .setFlags(Intent.FLAG_FROM_BACKGROUND); boolean binding = false; boolean startedWithForegroundFlag = false; try { final Context.BindServiceFlags bindFlags; if (job.shouldTreatAsUserInitiatedJob()) { if (job.shouldTreatAsUserInitiatedJob() && !job.isUserBgRestricted()) { // If the user has bg restricted the app, don't give the job FG privileges // such as bypassing data saver or getting the higher foreground proc state. // If we've gotten to this point, the app is most likely in the foreground, // so the job will run just fine while the user keeps the app in the foreground. bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS | Context.BIND_NOT_APP_COMPONENT_USAGE); } else if (job.shouldTreatAsExpeditedJob()) { startedWithForegroundFlag = true; } else if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND Loading Loading @@ -535,8 +541,11 @@ public final class JobServiceContext implements ServiceConnection { mAvailable = false; mStoppedReason = null; mStoppedTime = 0; // Wait until after bindService() returns a success value to set these so we don't // have JobStatus objects that aren't running but have these set to true. job.startedAsExpeditedJob = job.shouldTreatAsExpeditedJob(); job.startedAsUserInitiatedJob = job.shouldTreatAsUserInitiatedJob(); job.startedWithForegroundFlag = startedWithForegroundFlag; return true; } } Loading apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java +28 −4 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.job.controllers; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.os.SystemClock; import android.os.UserHandle; Loading Loading @@ -205,8 +206,32 @@ public final class BackgroundJobsController extends StateController { final int uid = jobStatus.getSourceUid(); final String packageName = jobStatus.getSourcePackageName(); final boolean canRun = !mAppStateTracker.areJobsRestricted(uid, packageName, jobStatus.canRunInBatterySaver()); final boolean isUserBgRestricted = !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled() && !mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, packageName); // If a job started with the foreground flag, it'll cause the UID to stay active // and thus cause areJobsRestricted() to always return false, so if // areJobsRestricted() returns false and the app is BG restricted and not TOP, // we need to stop any jobs that started with the foreground flag so they don't // keep the app in an elevated proc state. If we were to get in this situation, // then the user restricted the app after the job started, so it's best to stop // the job as soon as possible, especially since the job would be visible to the // user (with a notification and in Task Manager). // There are several other reasons that uidActive can be true for an app even if its // proc state is less important than BFGS. // JobScheduler has historically (at least up through UDC) allowed the app's jobs to run // when its UID was active, even if it's background restricted. This has been fine because // JobScheduler stops the job as soon as the UID becomes inactive and the jobs themselves // will not keep the UID active. The logic here is to ensure that special jobs // (e.g. user-initiated jobs) themselves do not keep the UID active when the app is // background restricted. final boolean shouldStopImmediately = jobStatus.startedWithForegroundFlag && isUserBgRestricted && mService.getUidProcState(uid) > ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; final boolean canRun = !shouldStopImmediately && !mAppStateTracker.areJobsRestricted( uid, packageName, jobStatus.canRunInBatterySaver()); final boolean isActive; if (activeState == UNKNOWN) { Loading @@ -219,8 +244,7 @@ public final class BackgroundJobsController extends StateController { } boolean didChange = jobStatus.setBackgroundNotRestrictedConstraintSatisfied(nowElapsed, canRun, !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled() && !mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, packageName)); isUserBgRestricted); didChange |= jobStatus.setUidActive(isActive); return didChange; } Loading apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +6 −0 Original line number Diff line number Diff line Loading @@ -1774,6 +1774,12 @@ public final class ConnectivityController extends RestrictingController implemen } pw.println(); if (mBackgroundMeteredAllowed.size() > 0) { pw.print("Background metered allowed: "); pw.println(mBackgroundMeteredAllowed); pw.println(); } pw.println("Current default network callbacks:"); pw.increaseIndent(); for (int i = 0; i < mCurrentDefaultNetworkCallbacks.size(); i++) { Loading apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +22 −0 Original line number Diff line number Diff line Loading @@ -430,6 +430,13 @@ public final class JobStatus { * when it started running. This isn't copied over when a job is rescheduled. */ public boolean startedAsUserInitiatedJob = false; /** * Whether this particular JobStatus instance started with the foreground flag * (or more accurately, did <b>not</b> have the * {@link android.content.Context#BIND_NOT_FOREGROUND} flag * included in its binding flags when started). */ public boolean startedWithForegroundFlag = false; public boolean startedWithImmediacyPrivilege = false; Loading Loading @@ -1606,6 +1613,10 @@ public final class JobStatus { * for any reason. */ public boolean shouldTreatAsUserInitiatedJob() { // isUserBgRestricted is intentionally excluded from this method. It should be fine to // treat the job as a UI job while the app is TOP, but just not in the background. // Instead of adding a proc state check here, the parts of JS that can make the distinction // and care about the distinction can do the check. return getJob().isUserInitiated() && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_USER) == 0 && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ) == 0; Loading Loading @@ -1653,6 +1664,11 @@ public final class JobStatus { && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0); } /** Returns whether or not the app is background restricted by the user (FAS). */ public boolean isUserBgRestricted() { return mIsUserBgRestricted; } /** @return true if the constraint was changed, false otherwise. */ boolean setChargingConstraintSatisfied(final long nowElapsed, boolean state) { return setConstraintSatisfied(CONSTRAINT_CHARGING, nowElapsed, state); Loading Loading @@ -2802,6 +2818,12 @@ public final class JobStatus { } pw.decreaseIndent(); pw.print("Started with foreground flag: "); pw.println(startedWithForegroundFlag); if (mIsUserBgRestricted) { pw.println("User BG restricted"); } if (changedAuthorities != null) { pw.println("Changed authorities:"); pw.increaseIndent(); Loading Loading
apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java +3 −0 Original line number Diff line number Diff line Loading @@ -200,7 +200,10 @@ class JobNotificationCoordinator { // No more jobs using this notification. Apply the final job stop policy. // If the user attempted to stop the job/app, then always remove the notification // so the user doesn't get confused about the app state. // Similarly, if the user background restricted the app, remove the notification so // the user doesn't think the app is continuing to run in the background. if (details.jobEndNotificationPolicy == JOB_END_NOTIFICATION_POLICY_REMOVE || stopReason == JobParameters.STOP_REASON_BACKGROUND_RESTRICTION || stopReason == JobParameters.STOP_REASON_USER) { mNotificationManagerInternal.cancelNotification( packageName, packageName, details.appUid, details.appPid, /* tag */ null, Loading
apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +11 −2 Original line number Diff line number Diff line Loading @@ -413,16 +413,22 @@ public final class JobServiceContext implements ServiceConnection { final Intent intent = new Intent().setComponent(job.getServiceComponent()) .setFlags(Intent.FLAG_FROM_BACKGROUND); boolean binding = false; boolean startedWithForegroundFlag = false; try { final Context.BindServiceFlags bindFlags; if (job.shouldTreatAsUserInitiatedJob()) { if (job.shouldTreatAsUserInitiatedJob() && !job.isUserBgRestricted()) { // If the user has bg restricted the app, don't give the job FG privileges // such as bypassing data saver or getting the higher foreground proc state. // If we've gotten to this point, the app is most likely in the foreground, // so the job will run just fine while the user keeps the app in the foreground. bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS | Context.BIND_NOT_APP_COMPONENT_USAGE); } else if (job.shouldTreatAsExpeditedJob()) { startedWithForegroundFlag = true; } else if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) { bindFlags = Context.BindServiceFlags.of( Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND Loading Loading @@ -535,8 +541,11 @@ public final class JobServiceContext implements ServiceConnection { mAvailable = false; mStoppedReason = null; mStoppedTime = 0; // Wait until after bindService() returns a success value to set these so we don't // have JobStatus objects that aren't running but have these set to true. job.startedAsExpeditedJob = job.shouldTreatAsExpeditedJob(); job.startedAsUserInitiatedJob = job.shouldTreatAsUserInitiatedJob(); job.startedWithForegroundFlag = startedWithForegroundFlag; return true; } } Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java +28 −4 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.server.job.controllers; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.os.SystemClock; import android.os.UserHandle; Loading Loading @@ -205,8 +206,32 @@ public final class BackgroundJobsController extends StateController { final int uid = jobStatus.getSourceUid(); final String packageName = jobStatus.getSourcePackageName(); final boolean canRun = !mAppStateTracker.areJobsRestricted(uid, packageName, jobStatus.canRunInBatterySaver()); final boolean isUserBgRestricted = !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled() && !mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, packageName); // If a job started with the foreground flag, it'll cause the UID to stay active // and thus cause areJobsRestricted() to always return false, so if // areJobsRestricted() returns false and the app is BG restricted and not TOP, // we need to stop any jobs that started with the foreground flag so they don't // keep the app in an elevated proc state. If we were to get in this situation, // then the user restricted the app after the job started, so it's best to stop // the job as soon as possible, especially since the job would be visible to the // user (with a notification and in Task Manager). // There are several other reasons that uidActive can be true for an app even if its // proc state is less important than BFGS. // JobScheduler has historically (at least up through UDC) allowed the app's jobs to run // when its UID was active, even if it's background restricted. This has been fine because // JobScheduler stops the job as soon as the UID becomes inactive and the jobs themselves // will not keep the UID active. The logic here is to ensure that special jobs // (e.g. user-initiated jobs) themselves do not keep the UID active when the app is // background restricted. final boolean shouldStopImmediately = jobStatus.startedWithForegroundFlag && isUserBgRestricted && mService.getUidProcState(uid) > ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; final boolean canRun = !shouldStopImmediately && !mAppStateTracker.areJobsRestricted( uid, packageName, jobStatus.canRunInBatterySaver()); final boolean isActive; if (activeState == UNKNOWN) { Loading @@ -219,8 +244,7 @@ public final class BackgroundJobsController extends StateController { } boolean didChange = jobStatus.setBackgroundNotRestrictedConstraintSatisfied(nowElapsed, canRun, !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled() && !mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, packageName)); isUserBgRestricted); didChange |= jobStatus.setUidActive(isActive); return didChange; } Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +6 −0 Original line number Diff line number Diff line Loading @@ -1774,6 +1774,12 @@ public final class ConnectivityController extends RestrictingController implemen } pw.println(); if (mBackgroundMeteredAllowed.size() > 0) { pw.print("Background metered allowed: "); pw.println(mBackgroundMeteredAllowed); pw.println(); } pw.println("Current default network callbacks:"); pw.increaseIndent(); for (int i = 0; i < mCurrentDefaultNetworkCallbacks.size(); i++) { Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +22 −0 Original line number Diff line number Diff line Loading @@ -430,6 +430,13 @@ public final class JobStatus { * when it started running. This isn't copied over when a job is rescheduled. */ public boolean startedAsUserInitiatedJob = false; /** * Whether this particular JobStatus instance started with the foreground flag * (or more accurately, did <b>not</b> have the * {@link android.content.Context#BIND_NOT_FOREGROUND} flag * included in its binding flags when started). */ public boolean startedWithForegroundFlag = false; public boolean startedWithImmediacyPrivilege = false; Loading Loading @@ -1606,6 +1613,10 @@ public final class JobStatus { * for any reason. */ public boolean shouldTreatAsUserInitiatedJob() { // isUserBgRestricted is intentionally excluded from this method. It should be fine to // treat the job as a UI job while the app is TOP, but just not in the background. // Instead of adding a proc state check here, the parts of JS that can make the distinction // and care about the distinction can do the check. return getJob().isUserInitiated() && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_USER) == 0 && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ) == 0; Loading Loading @@ -1653,6 +1664,11 @@ public final class JobStatus { && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0); } /** Returns whether or not the app is background restricted by the user (FAS). */ public boolean isUserBgRestricted() { return mIsUserBgRestricted; } /** @return true if the constraint was changed, false otherwise. */ boolean setChargingConstraintSatisfied(final long nowElapsed, boolean state) { return setConstraintSatisfied(CONSTRAINT_CHARGING, nowElapsed, state); Loading Loading @@ -2802,6 +2818,12 @@ public final class JobStatus { } pw.decreaseIndent(); pw.print("Started with foreground flag: "); pw.println(startedWithForegroundFlag); if (mIsUserBgRestricted) { pw.println("User BG restricted"); } if (changedAuthorities != null) { pw.println("Changed authorities:"); pw.increaseIndent(); Loading