Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 62429317 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Don't crash apps due to race condition." into sc-dev am: 776fea73 am: 533d81ba

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15361457

Change-Id: Ib1004aa3b176b88cef562270bd2f45a4d7c1022c
parents 158ced7d 533d81ba
Loading
Loading
Loading
Loading
+41 −4
Original line number Diff line number Diff line
@@ -116,6 +116,16 @@ public final class JobServiceContext implements ServiceConnection {
    @VisibleForTesting
    int mVerb;
    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.
@@ -439,7 +449,9 @@ public final class JobServiceContext implements ServiceConnection {
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mLock) {
                assertCallerLocked(cb);
                if (!assertCallerLocked(cb)) {
                    return null;
                }
                if (mVerb == VERB_STOPPING || mVerb == VERB_FINISHED) {
                    // 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
@@ -465,7 +477,11 @@ public final class JobServiceContext implements ServiceConnection {
        final long ident = Binder.clearCallingIdentity();
        try {
            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);
            }
        } finally {
@@ -554,18 +570,34 @@ public final class JobServiceContext implements ServiceConnection {
        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)) {
            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);
            sb.append("Caller no longer running");
            if (cb.mStoppedReason != null) {
                sb.append(", last stopped ");
                TimeUtils.formatDuration(sElapsedRealtimeClock.millis() - cb.mStoppedTime, sb);
                TimeUtils.formatDuration(nowElapsed - cb.mStoppedTime, sb);
                sb.append(" because: ");
                sb.append(cb.mStoppedReason);
            }
            throw new SecurityException(sb.toString());
        }
        return true;
    }

    /**
@@ -911,6 +943,11 @@ public final class JobServiceContext implements ServiceConnection {
        applyStoppedReasonLocked(reason);
        completedJob = mRunningJob;
        final int internalStopReason = mParams.getInternalStopReasonCode();
        mPreviousJobHadSuccessfulFinish =
                (internalStopReason == JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH);
        if (!mPreviousJobHadSuccessfulFinish) {
            mLastUnsuccessfulFinishElapsed = sElapsedRealtimeClock.millis();
        }
        mJobPackageTracker.noteInactive(completedJob, internalStopReason, reason);
        FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
                completedJob.getSourceUid(), null, completedJob.getBatteryName(),