Merge "Better handle uninstall cases." into sc-dev

This commit is contained in:
Kweku Adams
2021-04-07 21:27:16 +00:00
committed by Android (Google) Code Review
6 changed files with 84 additions and 55 deletions

View File

@@ -64,6 +64,16 @@ public class JobParameters implements Parcelable {
*/ */
public static final int REASON_RESTRICTED_BUCKET = public static final int REASON_RESTRICTED_BUCKET =
JobProtoEnums.STOP_REASON_RESTRICTED_BUCKET; // 6. JobProtoEnums.STOP_REASON_RESTRICTED_BUCKET; // 6.
/**
* The app was uninstalled.
* @hide
*/
public static final int DEBUG_REASON_UNINSTALL = 7;
/**
* The app's data was cleared.
* @hide
*/
public static final int DEBUG_REASON_DATA_CLEARED = 8;
/** /**
* All the stop reason codes. This should be regarded as an immutable array at runtime. * All the stop reason codes. This should be regarded as an immutable array at runtime.
@@ -187,8 +197,8 @@ public class JobParameters implements Parcelable {
*/ */
public static final int STOP_REASON_APP_STANDBY = 12; public static final int STOP_REASON_APP_STANDBY = 12;
/** /**
* The user stopped the job. This can happen either through force-stop, or via adb shell * The user stopped the job. This can happen either through force-stop, adb shell commands,
* commands. * or uninstalling.
*/ */
public static final int STOP_REASON_USER = 13; public static final int STOP_REASON_USER = 13;
/** The system is doing some processing that requires stopping this job. */ /** The system is doing some processing that requires stopping this job. */

View File

@@ -37,7 +37,8 @@ public interface JobSchedulerInternal {
/** /**
* Cancel the jobs for a given uid (e.g. when app data is cleared) * Cancel the jobs for a given uid (e.g. when app data is cleared)
*/ */
void cancelJobsForUid(int uid, @JobParameters.StopReason int reason, String debugReason); void cancelJobsForUid(int uid, @JobParameters.StopReason int reason, int debugReasonCode,
String debugReason);
/** /**
* These are for activity manager to communicate to use what is currently performing backups. * These are for activity manager to communicate to use what is currently performing backups.

View File

@@ -747,12 +747,14 @@ public class JobSchedulerService extends com.android.server.SystemService
+ " in user " + userId); + " in user " + userId);
} }
synchronized (mLock) { synchronized (mLock) {
// By the time we get here, the process should have // There's no guarantee that the process has been
// already been stopped, so the app wouldn't get the // stopped by the time we get here, but since this is
// stop reason, so just put USER instead of UNINSTALL // a user-initiated action, it should be fine to just
// or DISABLED. // put USER instead of UNINSTALL or DISABLED.
cancelJobsForPackageAndUidLocked(pkgName, pkgUid, cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
JobParameters.STOP_REASON_USER, "app disabled"); JobParameters.STOP_REASON_USER,
JobParameters.DEBUG_REASON_UNINSTALL,
"app disabled");
} }
} }
} catch (RemoteException|IllegalArgumentException e) { } catch (RemoteException|IllegalArgumentException e) {
@@ -783,25 +785,22 @@ public class JobSchedulerService extends com.android.server.SystemService
} else { } else {
Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid); Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
} }
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { } else if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
// If this is an outright uninstall rather than the first half of an int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
// app update sequence, cancel the jobs associated with the app. if (DEBUG) {
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1); }
if (DEBUG) { synchronized (mLock) {
Slog.d(TAG, "Removing jobs for uid: " + uidRemoved); // There's no guarantee that the process has been stopped by the time we
} // get here, but since this is generally a user-initiated action, it should
// By the time we get here, the process should have already // be fine to just put USER instead of UNINSTALL or DISABLED.
// been stopped, so the app wouldn't get the stop reason, cancelJobsForPackageAndUidLocked(pkgName, uidRemoved,
// so just put USER instead of UNINSTALL or DISABLED. JobParameters.STOP_REASON_USER,
synchronized (mLock) { JobParameters.DEBUG_REASON_UNINSTALL, "app uninstalled");
cancelJobsForPackageAndUidLocked(pkgName, uidRemoved, for (int c = 0; c < mControllers.size(); ++c) {
JobParameters.STOP_REASON_USER, "app uninstalled"); mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
for (int c = 0; c < mControllers.size(); ++c) {
mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
}
mDebuggableApps.remove(pkgName);
} }
mDebuggableApps.remove(pkgName);
} }
} else if (Intent.ACTION_USER_ADDED.equals(action)) { } else if (Intent.ACTION_USER_ADDED.equals(action)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
@@ -849,7 +848,8 @@ public class JobSchedulerService extends com.android.server.SystemService
} }
synchronized (mLock) { synchronized (mLock) {
cancelJobsForPackageAndUidLocked(pkgName, pkgUid, cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
JobParameters.STOP_REASON_USER, "app force stopped"); JobParameters.STOP_REASON_USER, JobParameters.REASON_CANCELED,
"app force stopped");
} }
} }
} }
@@ -1038,7 +1038,7 @@ public class JobSchedulerService extends com.android.server.SystemService
if (toCancel != null) { if (toCancel != null) {
// Implicitly replaces the existing job record with the new instance // Implicitly replaces the existing job record with the new instance
cancelJobImplLocked(toCancel, jobStatus, JobParameters.STOP_REASON_CANCELLED_BY_APP, cancelJobImplLocked(toCancel, jobStatus, JobParameters.STOP_REASON_CANCELLED_BY_APP,
"job rescheduled by app"); JobParameters.REASON_CANCELED, "job rescheduled by app");
} else { } else {
startTrackingJobLocked(jobStatus, null); startTrackingJobLocked(jobStatus, null);
} }
@@ -1115,10 +1115,11 @@ public class JobSchedulerService extends com.android.server.SystemService
final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle); final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
for (int i = 0; i < jobsForUser.size(); i++) { for (int i = 0; i < jobsForUser.size(); i++) {
JobStatus toRemove = jobsForUser.get(i); JobStatus toRemove = jobsForUser.get(i);
// By the time we get here, the process should have already been stopped, so the // There's no guarantee that the process has been stopped by the time we get here,
// app wouldn't get the stop reason, so just put USER instead of UNINSTALL. // but since this is a user-initiated action, it should be fine to just put USER
// instead of UNINSTALL or DISABLED.
cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER, cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER,
"user removed"); JobParameters.DEBUG_REASON_UNINSTALL, "user removed");
} }
} }
@@ -1130,7 +1131,7 @@ public class JobSchedulerService extends com.android.server.SystemService
} }
private void cancelJobsForPackageAndUidLocked(String pkgName, int uid, private void cancelJobsForPackageAndUidLocked(String pkgName, int uid,
@JobParameters.StopReason int reason, String debugReason) { @JobParameters.StopReason int reason, int debugReasonCode, String debugReason) {
if ("android".equals(pkgName)) { if ("android".equals(pkgName)) {
Slog.wtfStack(TAG, "Can't cancel all jobs for system package"); Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
return; return;
@@ -1139,7 +1140,7 @@ public class JobSchedulerService extends com.android.server.SystemService
for (int i = jobsForUid.size() - 1; i >= 0; i--) { for (int i = jobsForUid.size() - 1; i >= 0; i--) {
final JobStatus job = jobsForUid.get(i); final JobStatus job = jobsForUid.get(i);
if (job.getSourcePackageName().equals(pkgName)) { if (job.getSourcePackageName().equals(pkgName)) {
cancelJobImplLocked(job, null, reason, debugReason); cancelJobImplLocked(job, null, reason, debugReasonCode, debugReason);
} }
} }
} }
@@ -1152,7 +1153,7 @@ public class JobSchedulerService extends com.android.server.SystemService
* @param uid Uid to check against for removal of a job. * @param uid Uid to check against for removal of a job.
*/ */
public boolean cancelJobsForUid(int uid, @JobParameters.StopReason int reason, public boolean cancelJobsForUid(int uid, @JobParameters.StopReason int reason,
String debugReason) { int debugReasonCode, String debugReason) {
if (uid == Process.SYSTEM_UID) { if (uid == Process.SYSTEM_UID) {
Slog.wtfStack(TAG, "Can't cancel all jobs for system uid"); Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
return false; return false;
@@ -1161,9 +1162,9 @@ public class JobSchedulerService extends com.android.server.SystemService
boolean jobsCanceled = false; boolean jobsCanceled = false;
synchronized (mLock) { synchronized (mLock) {
final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid); final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
for (int i=0; i<jobsForUid.size(); i++) { for (int i = 0; i < jobsForUid.size(); i++) {
JobStatus toRemove = jobsForUid.get(i); JobStatus toRemove = jobsForUid.get(i);
cancelJobImplLocked(toRemove, null, reason, debugReason); cancelJobImplLocked(toRemove, null, reason, debugReasonCode, debugReason);
jobsCanceled = true; jobsCanceled = true;
} }
} }
@@ -1185,8 +1186,9 @@ public class JobSchedulerService extends com.android.server.SystemService
toCancel = mJobs.getJobByUidAndJobId(uid, jobId); toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
if (toCancel != null) { if (toCancel != null) {
cancelJobImplLocked(toCancel, null, reason, cancelJobImplLocked(toCancel, null, reason,
JobParameters.REASON_CANCELED,
"cancel() called by app, callingUid=" + callingUid "cancel() called by app, callingUid=" + callingUid
+ " uid=" + uid + " jobId=" + jobId); + " uid=" + uid + " jobId=" + jobId);
} }
return (toCancel != null); return (toCancel != null);
} }
@@ -1199,7 +1201,7 @@ public class JobSchedulerService extends com.android.server.SystemService
* currently scheduled jobs. * currently scheduled jobs.
*/ */
private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob,
@JobParameters.StopReason int reason, String debugReason) { @JobParameters.StopReason int reason, int debugReasonCode, String debugReason) {
if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString()); if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
cancelled.unprepareLocked(); cancelled.unprepareLocked();
stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */); stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
@@ -1208,8 +1210,7 @@ public class JobSchedulerService extends com.android.server.SystemService
mJobPackageTracker.noteNonpending(cancelled); mJobPackageTracker.noteNonpending(cancelled);
} }
// Cancel if running. // Cancel if running.
stopJobOnServiceContextLocked(cancelled, reason, JobParameters.REASON_CANCELED, stopJobOnServiceContextLocked(cancelled, reason, debugReasonCode, debugReason);
debugReason);
// If this is a replacement, bring in the new version of the job // If this is a replacement, bring in the new version of the job
if (incomingJob != null) { if (incomingJob != null) {
if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString()); if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString());
@@ -1459,7 +1460,7 @@ public class JobSchedulerService extends com.android.server.SystemService
Slog.v(TAG, " replacing " + oldJob + " with " + newJob); Slog.v(TAG, " replacing " + oldJob + " with " + newJob);
} }
cancelJobImplLocked(oldJob, newJob, JobParameters.STOP_REASON_SYSTEM_PROCESSING, cancelJobImplLocked(oldJob, newJob, JobParameters.STOP_REASON_SYSTEM_PROCESSING,
"deferred rtc calculation"); JobParameters.REASON_CANCELED, "deferred rtc calculation");
} }
} }
}; };
@@ -1482,7 +1483,7 @@ public class JobSchedulerService extends com.android.server.SystemService
// Register br for package removals and user removals. // Register br for package removals and user removals.
final IntentFilter filter = new IntentFilter(); final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
@@ -1786,15 +1787,26 @@ public class JobSchedulerService extends com.android.server.SystemService
* @param needsReschedule Whether the implementing class should reschedule this job. * @param needsReschedule Whether the implementing class should reschedule this job.
*/ */
@Override @Override
public void onJobCompletedLocked(JobStatus jobStatus, int stopReason, boolean needsReschedule) { public void onJobCompletedLocked(JobStatus jobStatus, int debugStopReason,
boolean needsReschedule) {
if (DEBUG) { if (DEBUG) {
Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule); Slog.d(TAG, "Completed " + jobStatus + ", reason=" + debugStopReason
+ ", reschedule=" + needsReschedule);
} }
mLastCompletedJobs[mLastCompletedJobIndex] = jobStatus; mLastCompletedJobs[mLastCompletedJobIndex] = jobStatus;
mLastCompletedJobTimeElapsed[mLastCompletedJobIndex] = sElapsedRealtimeClock.millis(); mLastCompletedJobTimeElapsed[mLastCompletedJobIndex] = sElapsedRealtimeClock.millis();
mLastCompletedJobIndex = (mLastCompletedJobIndex + 1) % NUM_COMPLETED_JOB_HISTORY; mLastCompletedJobIndex = (mLastCompletedJobIndex + 1) % NUM_COMPLETED_JOB_HISTORY;
if (debugStopReason == JobParameters.DEBUG_REASON_UNINSTALL
|| debugStopReason == JobParameters.DEBUG_REASON_DATA_CLEARED) {
// The job should have already been cleared from the rest of the JS tracking. No need
// to go through all that flow again.
jobStatus.unprepareLocked();
reportActiveLocked();
return;
}
// Intentionally not checking expedited job quota here. An app can't find out if it's run // Intentionally not checking expedited job quota here. An app can't find out if it's run
// out of quota when it asks JS to reschedule an expedited job. Instead, the rescheduled // out of quota when it asks JS to reschedule an expedited job. Instead, the rescheduled
// EJ will just be demoted to a regular job if the app has no EJ quota left. // EJ will just be demoted to a regular job if the app has no EJ quota left.
@@ -1805,8 +1817,8 @@ public class JobSchedulerService extends com.android.server.SystemService
final JobStatus rescheduledJob = needsReschedule final JobStatus rescheduledJob = needsReschedule
? getRescheduleJobForFailureLocked(jobStatus) : null; ? getRescheduleJobForFailureLocked(jobStatus) : null;
if (rescheduledJob != null if (rescheduledJob != null
&& (stopReason == JobParameters.REASON_TIMEOUT && (debugStopReason == JobParameters.REASON_TIMEOUT
|| stopReason == JobParameters.REASON_PREEMPT)) { || debugStopReason == JobParameters.REASON_PREEMPT)) {
rescheduledJob.disallowRunInBatterySaverAndDoze(); rescheduledJob.disallowRunInBatterySaverAndDoze();
} }
@@ -1911,6 +1923,7 @@ public class JobSchedulerService extends com.android.server.SystemService
break; break;
case MSG_STOP_JOB: case MSG_STOP_JOB:
cancelJobImplLocked((JobStatus) message.obj, null, message.arg1, cancelJobImplLocked((JobStatus) message.obj, null, message.arg1,
JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
"app no longer allowed to run"); "app no longer allowed to run");
break; break;
@@ -1927,7 +1940,7 @@ public class JobSchedulerService extends com.android.server.SystemService
if (disabled) { if (disabled) {
cancelJobsForUid(uid, cancelJobsForUid(uid,
JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
"uid gone"); JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED, "uid gone");
} }
synchronized (mLock) { synchronized (mLock) {
mDeviceIdleJobsController.setUidActiveLocked(uid, false); mDeviceIdleJobsController.setUidActiveLocked(uid, false);
@@ -1947,7 +1960,7 @@ public class JobSchedulerService extends com.android.server.SystemService
if (disabled) { if (disabled) {
cancelJobsForUid(uid, cancelJobsForUid(uid,
JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
"app uid idle"); JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED, "app uid idle");
} }
synchronized (mLock) { synchronized (mLock) {
mDeviceIdleJobsController.setUidActiveLocked(uid, false); mDeviceIdleJobsController.setUidActiveLocked(uid, false);
@@ -2409,8 +2422,8 @@ public class JobSchedulerService extends com.android.server.SystemService
@Override @Override
public void cancelJobsForUid(int uid, @JobParameters.StopReason int reason, public void cancelJobsForUid(int uid, @JobParameters.StopReason int reason,
String debugReason) { int debugReasonCode, String debugReason) {
JobSchedulerService.this.cancelJobsForUid(uid, reason, debugReason); JobSchedulerService.this.cancelJobsForUid(uid, reason, debugReasonCode, debugReason);
} }
@Override @Override
@@ -2749,6 +2762,7 @@ public class JobSchedulerService extends com.android.server.SystemService
try { try {
JobSchedulerService.this.cancelJobsForUid(uid, JobSchedulerService.this.cancelJobsForUid(uid,
JobParameters.STOP_REASON_CANCELLED_BY_APP, JobParameters.STOP_REASON_CANCELLED_BY_APP,
JobParameters.REASON_CANCELED,
"cancelAll() called by app, callingUid=" + uid); "cancelAll() called by app, callingUid=" + uid);
} finally { } finally {
Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident);
@@ -2969,7 +2983,7 @@ public class JobSchedulerService extends com.android.server.SystemService
if (!hasJobId) { if (!hasJobId) {
pw.println("Canceling all jobs for " + pkgName + " in user " + userId); pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
if (!cancelJobsForUid(pkgUid, JobParameters.STOP_REASON_USER, if (!cancelJobsForUid(pkgUid, JobParameters.STOP_REASON_USER,
"cancel shell command for package")) { JobParameters.REASON_CANCELED, "cancel shell command for package")) {
pw.println("No matching jobs found."); pw.println("No matching jobs found.");
} }
} else { } else {

View File

@@ -244,7 +244,10 @@ public final class ConnectivityController extends RestrictingController implemen
getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true); getUidStats(jobStatus.getSourceUid(), jobStatus.getSourcePackageName(), true);
uidStats.numReadyWithConnectivity--; uidStats.numReadyWithConnectivity--;
if (jobStatus.madeActive != 0) { if (jobStatus.madeActive != 0) {
uidStats.numRunning--; // numRunning would be 0 if the UidStats object didn't exist before this method
// was called. getUidStats() handles logging, so just make sure we don't save a
// negative value.
uidStats.numRunning = Math.max(0, uidStats.numRunning - 1);
} }
maybeRevokeStandbyExceptionLocked(jobStatus); maybeRevokeStandbyExceptionLocked(jobStatus);
postAdjustCallbacks(); postAdjustCallbacks();

View File

@@ -735,6 +735,7 @@ public final class QuotaController extends StateController {
mTempAllowlistCache.delete(uid); mTempAllowlistCache.delete(uid);
mTempAllowlistGraceCache.delete(uid); mTempAllowlistGraceCache.delete(uid);
mTopAppCache.delete(uid); mTopAppCache.delete(uid);
mTopAppTrackers.delete(uid);
mTopAppGraceCache.delete(uid); mTopAppGraceCache.delete(uid);
} }

View File

@@ -3489,9 +3489,9 @@ public class ActivityManagerService extends IActivityManager.Stub
// Clear its scheduled jobs // Clear its scheduled jobs
JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class); JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
// Clearing data is akin to uninstalling. The app is force stopped before we // Clearing data is a user-initiated action.
// get to this point, so the reason won't be checked by the app. js.cancelJobsForUid(appInfo.uid, JobParameters.STOP_REASON_USER,
js.cancelJobsForUid(appInfo.uid, JobParameters.STOP_REASON_USER, "clear data"); JobParameters.DEBUG_REASON_DATA_CLEARED, "clear data");
// Clear its pending alarms // Clear its pending alarms
AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class); AlarmManagerInternal ami = LocalServices.getService(AlarmManagerInternal.class);