Loading apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +5 −3 Original line number Diff line number Diff line Loading @@ -1433,10 +1433,10 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid); } synchronized (mLock) { // Exclude jobs scheduled on behalf of this app for now because SyncManager // Exclude jobs scheduled on behalf of this app because SyncManager // and other job proxy agents may not know to reschedule the job properly // after force stop. // TODO(209852664): determine how to best handle syncs & other proxied jobs // Proxied jobs will not be allowed to run if the source app is stopped. cancelJobsForPackageAndUidLocked(pkgName, pkgUid, /* includeSchedulingApp */ true, /* includeSourceApp */ false, JobParameters.STOP_REASON_USER, Loading @@ -1448,7 +1448,9 @@ public class JobSchedulerService extends com.android.server.SystemService } }; private String getPackageName(Intent intent) { /** Returns the package name stored in the intent's data. */ @Nullable public static String getPackageName(Intent intent) { Uri uri = intent.getData(); String pkg = uri != null ? uri.getSchemeSpecificPart() : null; return pkg; Loading apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java +115 −4 Original line number Diff line number Diff line Loading @@ -17,18 +17,26 @@ package com.android.server.job.controllers; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.getPackageName; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManagerInternal; import android.os.SystemClock; import android.os.UserHandle; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; import android.util.SparseArrayMap; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.server.AppStateTracker; import com.android.server.AppStateTrackerImpl; import com.android.server.AppStateTrackerImpl.Listener; Loading @@ -50,6 +58,8 @@ import java.util.function.Predicate; * * - the uid-active boolean state expressed by the AppStateTracker. Jobs in 'active' * uids are inherently eligible to run jobs regardless of the uid's standby bucket. * * - the app's stopped state */ public final class BackgroundJobsController extends StateController { private static final String TAG = "JobScheduler.Background"; Loading @@ -63,9 +73,48 @@ public final class BackgroundJobsController extends StateController { private final ActivityManagerInternal mActivityManagerInternal; private final AppStateTrackerImpl mAppStateTracker; private final PackageManagerInternal mPackageManagerInternal; @GuardedBy("mLock") private final SparseArrayMap<String, Boolean> mPackageStoppedState = new SparseArrayMap<>(); private final UpdateJobFunctor mUpdateJobFunctor = new UpdateJobFunctor(); private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String pkgName = getPackageName(intent); final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1); final String action = intent.getAction(); if (pkgUid == -1) { Slog.e(TAG, "Didn't get package UID in intent (" + action + ")"); return; } if (DEBUG) { Slog.d(TAG, "Got " + action + " for " + pkgUid + "/" + pkgName); } switch (action) { case Intent.ACTION_PACKAGE_RESTARTED: { synchronized (mLock) { mPackageStoppedState.add(pkgUid, pkgName, Boolean.TRUE); updateJobRestrictionsForUidLocked(pkgUid, false); } } break; case Intent.ACTION_PACKAGE_UNSTOPPED: { synchronized (mLock) { mPackageStoppedState.add(pkgUid, pkgName, Boolean.FALSE); updateJobRestrictionsLocked(pkgUid, UNKNOWN); } } break; } } }; public BackgroundJobsController(JobSchedulerService service) { super(service); Loading @@ -73,11 +122,18 @@ public final class BackgroundJobsController extends StateController { LocalServices.getService(ActivityManagerInternal.class)); mAppStateTracker = (AppStateTrackerImpl) Objects.requireNonNull( LocalServices.getService(AppStateTracker.class)); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); } @Override public void startTrackingLocked() { mAppStateTracker.addListener(mForceAppStandbyListener); final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); filter.addAction(Intent.ACTION_PACKAGE_UNSTOPPED); filter.addDataScheme("package"); mContext.registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, filter, null, null); } @Override Loading @@ -98,12 +154,46 @@ public final class BackgroundJobsController extends StateController { } } @Override public void onAppRemovedLocked(String packageName, int uid) { mPackageStoppedState.delete(uid, packageName); } @Override public void onUserRemovedLocked(int userId) { for (int u = mPackageStoppedState.numMaps() - 1; u >= 0; --u) { final int uid = mPackageStoppedState.keyAt(u); if (UserHandle.getUserId(uid) == userId) { mPackageStoppedState.deleteAt(u); } } } @Override public void dumpControllerStateLocked(final IndentingPrintWriter pw, final Predicate<JobStatus> predicate) { pw.println("Aconfig flags:"); pw.increaseIndent(); pw.print(android.content.pm.Flags.FLAG_STAY_STOPPED, android.content.pm.Flags.stayStopped()); pw.println(); pw.decreaseIndent(); pw.println(); mAppStateTracker.dump(pw); pw.println(); pw.println("Stopped packages:"); pw.increaseIndent(); mPackageStoppedState.forEach((uid, pkgName, isStopped) -> { pw.print(uid); pw.print(":"); pw.print(pkgName); pw.print("="); pw.println(isStopped); }); pw.println(); mService.getJobStore().forEachJob(predicate, (jobStatus) -> { final int uid = jobStatus.getSourceUid(); final String sourcePkg = jobStatus.getSourcePackageName(); Loading Loading @@ -205,14 +295,34 @@ public final class BackgroundJobsController extends StateController { } } private boolean isPackageStopped(String packageName, int uid) { if (mPackageStoppedState.contains(uid, packageName)) { return mPackageStoppedState.get(uid, packageName); } final boolean isStopped = mPackageManagerInternal.isPackageStopped(packageName, uid); mPackageStoppedState.add(uid, packageName, isStopped); return isStopped; } boolean updateSingleJobRestrictionLocked(JobStatus jobStatus, final long nowElapsed, int activeState) { final int uid = jobStatus.getSourceUid(); final String packageName = jobStatus.getSourcePackageName(); final boolean isUserBgRestricted = !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled() && !mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, packageName); final boolean isSourcePkgStopped = isPackageStopped(jobStatus.getSourcePackageName(), jobStatus.getSourceUid()); final boolean isCallingPkgStopped; if (!jobStatus.isProxyJob()) { isCallingPkgStopped = isSourcePkgStopped; } else { isCallingPkgStopped = isPackageStopped(jobStatus.getCallingPackageName(), jobStatus.getUid()); } final boolean isStopped = android.content.pm.Flags.stayStopped() && (isCallingPkgStopped || isSourcePkgStopped); final boolean isUserBgRestricted = isStopped || (!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, Loading @@ -233,7 +343,8 @@ public final class BackgroundJobsController extends StateController { && isUserBgRestricted && mService.getUidProcState(uid) > ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; final boolean canRun = !shouldStopImmediately // Don't let jobs (including proxied jobs) run if the app is in the stopped state. final boolean canRun = !isStopped && !shouldStopImmediately && !mAppStateTracker.areJobsRestricted( uid, packageName, jobStatus.canRunInBatterySaver()); Loading apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +6 −0 Original line number Diff line number Diff line Loading @@ -1102,6 +1102,12 @@ public final class JobStatus { return job.getService(); } /** Return the package name of the app that scheduled the job. */ public String getCallingPackageName() { return job.getService().getPackageName(); } /** Return the package name of the app on whose behalf the job was scheduled. */ public String getSourcePackageName() { return sourcePackageName; } Loading core/java/android/content/Intent.java +1 −1 Original line number Diff line number Diff line Loading @@ -2805,7 +2805,7 @@ public class Intent implements Parcelable, Cloneable { * and the package in the stopped state cannot self-start for any reason unless there's an * explicit request to start a component in the package. The {@link #ACTION_PACKAGE_UNSTOPPED} * broadcast is sent when such an explicit process start occurs and the package is taken * out of the stopped state. * out of the stopped state. The data contains the name of the package. * </p> * <ul> * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. Loading services/core/java/com/android/server/content/SyncJobService.java +0 −16 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.server.content; import android.annotation.Nullable; import android.app.job.JobParameters; import android.app.job.JobService; import android.content.pm.PackageManagerInternal; import android.os.Message; import android.os.SystemClock; import android.util.Log; Loading @@ -29,7 +28,6 @@ import android.util.SparseBooleanArray; import android.util.SparseLongArray; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; public class SyncJobService extends JobService { private static final String TAG = "SyncManager"; Loading Loading @@ -99,20 +97,6 @@ public class SyncJobService extends JobService { return true; } // TODO(b/209852664): remove this logic from here once it's added within JobScheduler. // JobScheduler should not call onStartJob for syncs whose source packages are stopped. // Until JS adds the relevant logic, this is a temporary solution to keep deferring syncs // for packages in the stopped state. if (android.content.pm.Flags.stayStopped()) { if (LocalServices.getService(PackageManagerInternal.class) .isPackageStopped(op.owningPackage, op.target.userId)) { if (Log.isLoggable(TAG, Log.DEBUG)) { Slog.d(TAG, "Skipping sync for force-stopped package: " + op.owningPackage); } return false; } } boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); synchronized (sLock) { final int jobId = params.getJobId(); Loading Loading
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +5 −3 Original line number Diff line number Diff line Loading @@ -1433,10 +1433,10 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid); } synchronized (mLock) { // Exclude jobs scheduled on behalf of this app for now because SyncManager // Exclude jobs scheduled on behalf of this app because SyncManager // and other job proxy agents may not know to reschedule the job properly // after force stop. // TODO(209852664): determine how to best handle syncs & other proxied jobs // Proxied jobs will not be allowed to run if the source app is stopped. cancelJobsForPackageAndUidLocked(pkgName, pkgUid, /* includeSchedulingApp */ true, /* includeSourceApp */ false, JobParameters.STOP_REASON_USER, Loading @@ -1448,7 +1448,9 @@ public class JobSchedulerService extends com.android.server.SystemService } }; private String getPackageName(Intent intent) { /** Returns the package name stored in the intent's data. */ @Nullable public static String getPackageName(Intent intent) { Uri uri = intent.getData(); String pkg = uri != null ? uri.getSchemeSpecificPart() : null; return pkg; Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java +115 −4 Original line number Diff line number Diff line Loading @@ -17,18 +17,26 @@ package com.android.server.job.controllers; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.getPackageName; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManagerInternal; import android.os.SystemClock; import android.os.UserHandle; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; import android.util.SparseArrayMap; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.server.AppStateTracker; import com.android.server.AppStateTrackerImpl; import com.android.server.AppStateTrackerImpl.Listener; Loading @@ -50,6 +58,8 @@ import java.util.function.Predicate; * * - the uid-active boolean state expressed by the AppStateTracker. Jobs in 'active' * uids are inherently eligible to run jobs regardless of the uid's standby bucket. * * - the app's stopped state */ public final class BackgroundJobsController extends StateController { private static final String TAG = "JobScheduler.Background"; Loading @@ -63,9 +73,48 @@ public final class BackgroundJobsController extends StateController { private final ActivityManagerInternal mActivityManagerInternal; private final AppStateTrackerImpl mAppStateTracker; private final PackageManagerInternal mPackageManagerInternal; @GuardedBy("mLock") private final SparseArrayMap<String, Boolean> mPackageStoppedState = new SparseArrayMap<>(); private final UpdateJobFunctor mUpdateJobFunctor = new UpdateJobFunctor(); private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String pkgName = getPackageName(intent); final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1); final String action = intent.getAction(); if (pkgUid == -1) { Slog.e(TAG, "Didn't get package UID in intent (" + action + ")"); return; } if (DEBUG) { Slog.d(TAG, "Got " + action + " for " + pkgUid + "/" + pkgName); } switch (action) { case Intent.ACTION_PACKAGE_RESTARTED: { synchronized (mLock) { mPackageStoppedState.add(pkgUid, pkgName, Boolean.TRUE); updateJobRestrictionsForUidLocked(pkgUid, false); } } break; case Intent.ACTION_PACKAGE_UNSTOPPED: { synchronized (mLock) { mPackageStoppedState.add(pkgUid, pkgName, Boolean.FALSE); updateJobRestrictionsLocked(pkgUid, UNKNOWN); } } break; } } }; public BackgroundJobsController(JobSchedulerService service) { super(service); Loading @@ -73,11 +122,18 @@ public final class BackgroundJobsController extends StateController { LocalServices.getService(ActivityManagerInternal.class)); mAppStateTracker = (AppStateTrackerImpl) Objects.requireNonNull( LocalServices.getService(AppStateTracker.class)); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); } @Override public void startTrackingLocked() { mAppStateTracker.addListener(mForceAppStandbyListener); final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); filter.addAction(Intent.ACTION_PACKAGE_UNSTOPPED); filter.addDataScheme("package"); mContext.registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, filter, null, null); } @Override Loading @@ -98,12 +154,46 @@ public final class BackgroundJobsController extends StateController { } } @Override public void onAppRemovedLocked(String packageName, int uid) { mPackageStoppedState.delete(uid, packageName); } @Override public void onUserRemovedLocked(int userId) { for (int u = mPackageStoppedState.numMaps() - 1; u >= 0; --u) { final int uid = mPackageStoppedState.keyAt(u); if (UserHandle.getUserId(uid) == userId) { mPackageStoppedState.deleteAt(u); } } } @Override public void dumpControllerStateLocked(final IndentingPrintWriter pw, final Predicate<JobStatus> predicate) { pw.println("Aconfig flags:"); pw.increaseIndent(); pw.print(android.content.pm.Flags.FLAG_STAY_STOPPED, android.content.pm.Flags.stayStopped()); pw.println(); pw.decreaseIndent(); pw.println(); mAppStateTracker.dump(pw); pw.println(); pw.println("Stopped packages:"); pw.increaseIndent(); mPackageStoppedState.forEach((uid, pkgName, isStopped) -> { pw.print(uid); pw.print(":"); pw.print(pkgName); pw.print("="); pw.println(isStopped); }); pw.println(); mService.getJobStore().forEachJob(predicate, (jobStatus) -> { final int uid = jobStatus.getSourceUid(); final String sourcePkg = jobStatus.getSourcePackageName(); Loading Loading @@ -205,14 +295,34 @@ public final class BackgroundJobsController extends StateController { } } private boolean isPackageStopped(String packageName, int uid) { if (mPackageStoppedState.contains(uid, packageName)) { return mPackageStoppedState.get(uid, packageName); } final boolean isStopped = mPackageManagerInternal.isPackageStopped(packageName, uid); mPackageStoppedState.add(uid, packageName, isStopped); return isStopped; } boolean updateSingleJobRestrictionLocked(JobStatus jobStatus, final long nowElapsed, int activeState) { final int uid = jobStatus.getSourceUid(); final String packageName = jobStatus.getSourcePackageName(); final boolean isUserBgRestricted = !mActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled() && !mAppStateTracker.isRunAnyInBackgroundAppOpsAllowed(uid, packageName); final boolean isSourcePkgStopped = isPackageStopped(jobStatus.getSourcePackageName(), jobStatus.getSourceUid()); final boolean isCallingPkgStopped; if (!jobStatus.isProxyJob()) { isCallingPkgStopped = isSourcePkgStopped; } else { isCallingPkgStopped = isPackageStopped(jobStatus.getCallingPackageName(), jobStatus.getUid()); } final boolean isStopped = android.content.pm.Flags.stayStopped() && (isCallingPkgStopped || isSourcePkgStopped); final boolean isUserBgRestricted = isStopped || (!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, Loading @@ -233,7 +343,8 @@ public final class BackgroundJobsController extends StateController { && isUserBgRestricted && mService.getUidProcState(uid) > ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; final boolean canRun = !shouldStopImmediately // Don't let jobs (including proxied jobs) run if the app is in the stopped state. final boolean canRun = !isStopped && !shouldStopImmediately && !mAppStateTracker.areJobsRestricted( uid, packageName, jobStatus.canRunInBatterySaver()); Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +6 −0 Original line number Diff line number Diff line Loading @@ -1102,6 +1102,12 @@ public final class JobStatus { return job.getService(); } /** Return the package name of the app that scheduled the job. */ public String getCallingPackageName() { return job.getService().getPackageName(); } /** Return the package name of the app on whose behalf the job was scheduled. */ public String getSourcePackageName() { return sourcePackageName; } Loading
core/java/android/content/Intent.java +1 −1 Original line number Diff line number Diff line Loading @@ -2805,7 +2805,7 @@ public class Intent implements Parcelable, Cloneable { * and the package in the stopped state cannot self-start for any reason unless there's an * explicit request to start a component in the package. The {@link #ACTION_PACKAGE_UNSTOPPED} * broadcast is sent when such an explicit process start occurs and the package is taken * out of the stopped state. * out of the stopped state. The data contains the name of the package. * </p> * <ul> * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. Loading
services/core/java/com/android/server/content/SyncJobService.java +0 −16 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.server.content; import android.annotation.Nullable; import android.app.job.JobParameters; import android.app.job.JobService; import android.content.pm.PackageManagerInternal; import android.os.Message; import android.os.SystemClock; import android.util.Log; Loading @@ -29,7 +28,6 @@ import android.util.SparseBooleanArray; import android.util.SparseLongArray; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalServices; public class SyncJobService extends JobService { private static final String TAG = "SyncManager"; Loading Loading @@ -99,20 +97,6 @@ public class SyncJobService extends JobService { return true; } // TODO(b/209852664): remove this logic from here once it's added within JobScheduler. // JobScheduler should not call onStartJob for syncs whose source packages are stopped. // Until JS adds the relevant logic, this is a temporary solution to keep deferring syncs // for packages in the stopped state. if (android.content.pm.Flags.stayStopped()) { if (LocalServices.getService(PackageManagerInternal.class) .isPackageStopped(op.owningPackage, op.target.userId)) { if (Log.isLoggable(TAG, Log.DEBUG)) { Slog.d(TAG, "Skipping sync for force-stopped package: " + op.owningPackage); } return false; } } boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE); synchronized (sLock) { final int jobId = params.getJobId(); Loading