Merge "Don't crash apps due to race condition." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
776fea73bb
@@ -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(),
|
||||||
|
|||||||
Reference in New Issue
Block a user