Merge "Don't crash apps due to race condition." into sc-dev

This commit is contained in:
TreeHugger Robot
2021-07-23 04:04:35 +00:00
committed by Android (Google) Code Review

View File

@@ -116,6 +116,16 @@ public final class JobServiceContext implements ServiceConnection {
@VisibleForTesting @VisibleForTesting
int mVerb; int mVerb;
private boolean mCancelled; private boolean mCancelled;
/**
* True if the previous job on this context successfully finished (ie. called jobFinished or
* dequeueWork with no work left).
*/
private boolean mPreviousJobHadSuccessfulFinish;
/**
* The last time a job on this context didn't finish successfully, in the elapsed realtime
* timebase.
*/
private long mLastUnsuccessfulFinishElapsed;
/** /**
* All the information maintained about the job currently being executed. * All the information maintained about the job currently being executed.
@@ -439,7 +449,9 @@ public final class JobServiceContext implements ServiceConnection {
final long ident = Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();
try { try {
synchronized (mLock) { synchronized (mLock) {
assertCallerLocked(cb); if (!assertCallerLocked(cb)) {
return null;
}
if (mVerb == VERB_STOPPING || mVerb == VERB_FINISHED) { if (mVerb == VERB_STOPPING || mVerb == VERB_FINISHED) {
// This job is either all done, or on its way out. Either way, it // This job is either all done, or on its way out. Either way, it
// should not dispatch any more work. We will pick up any remaining // should not dispatch any more work. We will pick up any remaining
@@ -465,7 +477,11 @@ public final class JobServiceContext implements ServiceConnection {
final long ident = Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();
try { try {
synchronized (mLock) { synchronized (mLock) {
assertCallerLocked(cb); if (!assertCallerLocked(cb)) {
// Return true instead of false here so we don't just kick the
// Exception-throwing-can down the road to JobParameters.completeWork >:(
return true;
}
return mRunningJob.completeWorkLocked(workId); return mRunningJob.completeWorkLocked(workId);
} }
} finally { } finally {
@@ -554,18 +570,34 @@ public final class JobServiceContext implements ServiceConnection {
return true; return true;
} }
private void assertCallerLocked(JobCallback cb) { /**
* Will throw a {@link SecurityException} if the callback is not for the currently running job,
* but may decide not to throw an exception if the call from the previous job appears to be an
* accident.
*
* @return true if the callback is for the current job, false otherwise
*/
private boolean assertCallerLocked(JobCallback cb) {
if (!verifyCallerLocked(cb)) { if (!verifyCallerLocked(cb)) {
final long nowElapsed = sElapsedRealtimeClock.millis();
if (!mPreviousJobHadSuccessfulFinish
&& (nowElapsed - mLastUnsuccessfulFinishElapsed) < 15_000L) {
// Don't punish apps for race conditions
return false;
}
// It's been long enough that the app should really not be calling into JS for the
// stopped job.
StringBuilder sb = new StringBuilder(128); StringBuilder sb = new StringBuilder(128);
sb.append("Caller no longer running"); sb.append("Caller no longer running");
if (cb.mStoppedReason != null) { if (cb.mStoppedReason != null) {
sb.append(", last stopped "); sb.append(", last stopped ");
TimeUtils.formatDuration(sElapsedRealtimeClock.millis() - cb.mStoppedTime, sb); TimeUtils.formatDuration(nowElapsed - cb.mStoppedTime, sb);
sb.append(" because: "); sb.append(" because: ");
sb.append(cb.mStoppedReason); sb.append(cb.mStoppedReason);
} }
throw new SecurityException(sb.toString()); throw new SecurityException(sb.toString());
} }
return true;
} }
/** /**
@@ -911,6 +943,11 @@ public final class JobServiceContext implements ServiceConnection {
applyStoppedReasonLocked(reason); applyStoppedReasonLocked(reason);
completedJob = mRunningJob; completedJob = mRunningJob;
final int internalStopReason = mParams.getInternalStopReasonCode(); final int internalStopReason = mParams.getInternalStopReasonCode();
mPreviousJobHadSuccessfulFinish =
(internalStopReason == JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
if (!mPreviousJobHadSuccessfulFinish) {
mLastUnsuccessfulFinishElapsed = sElapsedRealtimeClock.millis();
}
mJobPackageTracker.noteInactive(completedJob, internalStopReason, reason); mJobPackageTracker.noteInactive(completedJob, internalStopReason, reason);
FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED, FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
completedJob.getSourceUid(), null, completedJob.getBatteryName(), completedJob.getSourceUid(), null, completedJob.getBatteryName(),