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

Commit 693ce77e 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: 98d8c4c1

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

Change-Id: Iea451d076078bde46228432423e6ea24593d37f3
parents 52aa7a6a 98d8c4c1
Loading
Loading
Loading
Loading
+41 −4
Original line number Original line Diff line number Diff line
@@ -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.
@@ -447,7 +457,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
@@ -473,7 +485,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 {
@@ -530,18 +546,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;
    }
    }


    /**
    /**
@@ -887,6 +919,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(),