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

Commit 98d8c4c1 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

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

Change-Id: I522b9fb4a088ab4eecf9c3aba65b9d9514079b82
parents e2902b27 776fea73
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(),