From 729a328aca436d71b80f3d72f5d54e38d4d2c12e Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 9 Jun 2017 16:06:01 -0700 Subject: [PATCH] Work on issue #62390590: SecurityException in JobIntentService$... ...JobServiceEngineImpl$WrapperWorkItem.complete Add more useful information in the security exception message that is shown -- the reason the last job that was running on the context was stopped. This should tell you why when you are calling at that point your job is no longer running. Test: bit CtsJobSchedulerTestCases:* Change-Id: Ia7155248b4b4f032cbf8e8754c5437f658ed192c --- .../server/job/JobSchedulerService.java | 48 +++++--- .../android/server/job/JobServiceContext.java | 109 +++++++++++------- 2 files changed, 98 insertions(+), 59 deletions(-) diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index b0d76e854af23..b8fe88439c2d8 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -524,7 +524,7 @@ public final class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "Removing jobs for package " + pkgName + " in user " + userId); } - cancelJobsForUid(pkgUid); + cancelJobsForUid(pkgUid, "app package state changed"); } } catch (RemoteException|IllegalArgumentException e) { /* @@ -553,7 +553,7 @@ public final class JobSchedulerService extends com.android.server.SystemService if (DEBUG) { Slog.d(TAG, "Removing jobs for uid: " + uidRemoved); } - cancelJobsForUid(uidRemoved); + cancelJobsForUid(uidRemoved, "app uninstalled"); } } else if (Intent.ACTION_USER_REMOVED.equals(action)) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); @@ -611,7 +611,7 @@ public final class JobSchedulerService extends com.android.server.SystemService @Override public void onUidGone(int uid, boolean disabled) throws RemoteException { updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); if (disabled) { - cancelJobsForUid(uid); + cancelJobsForUid(uid, "uid gone"); } } @@ -620,7 +620,7 @@ public final class JobSchedulerService extends com.android.server.SystemService @Override public void onUidIdle(int uid, boolean disabled) throws RemoteException { if (disabled) { - cancelJobsForUid(uid); + cancelJobsForUid(uid, "app uid idle"); } } }; @@ -689,7 +689,7 @@ public final class JobSchedulerService extends com.android.server.SystemService jobStatus.prepareLocked(ActivityManager.getService()); if (toCancel != null) { - cancelJobImplLocked(toCancel, jobStatus); + cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app"); } if (work != null) { // If work has been supplied, enqueue it into the new job. @@ -747,7 +747,7 @@ public final class JobSchedulerService extends com.android.server.SystemService final List jobsForUser = mJobs.getJobsByUser(userHandle); for (int i=0; i= 0; i--) { final JobStatus job = jobsForUid.get(i); if (job.getSourcePackageName().equals(pkgName)) { - cancelJobImplLocked(job, null); + cancelJobImplLocked(job, null, "app force stopped"); } } } @@ -778,12 +778,12 @@ public final class JobSchedulerService extends com.android.server.SystemService * @param uid Uid to check against for removal of a job. * */ - public void cancelJobsForUid(int uid) { + public void cancelJobsForUid(int uid, String reason) { synchronized (mLock) { final List jobsForUid = mJobs.getJobsByUid(uid); for (int i=0; i Error * _PENDING -> Error */ - private void handleFinishedLocked(boolean reschedule) { + private void handleFinishedLocked(boolean reschedule, String reason) { switch (mVerb) { case VERB_EXECUTING: case VERB_STOPPING: - closeAndCleanupJobLocked(reschedule); + closeAndCleanupJobLocked(reschedule, reason); break; default: Slog.e(TAG, "Got an execution complete message for a job that wasn't being" + @@ -544,7 +564,7 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne * in the message queue. * _ENDING -> No point in doing anything here, so we ignore. */ - private void handleCancelLocked() { + private void handleCancelLocked(String reason) { if (JobSchedulerService.DEBUG) { Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " " + VERB_STRINGS[mVerb]); @@ -553,9 +573,10 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne case VERB_BINDING: case VERB_STARTING: mCancelled = true; + applyStoppedReasonLocked(reason); break; case VERB_EXECUTING: - sendStopMessageLocked(); + sendStopMessageLocked(reason); break; case VERB_STOPPING: // Nada. @@ -572,7 +593,7 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne case VERB_BINDING: Slog.e(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() + ", dropping."); - closeAndCleanupJobLocked(false /* needsReschedule */); + closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while binding"); break; case VERB_STARTING: // Client unresponsive - wedged or failed to respond in time. We don't really @@ -580,25 +601,25 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne // FINISHED/NO-RETRY. Slog.e(TAG, "No response from client for onStartJob '" + mRunningJob.toShortString()); - closeAndCleanupJobLocked(false /* needsReschedule */); + closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while starting"); break; case VERB_STOPPING: // At least we got somewhere, so fail but ask the JobScheduler to reschedule. Slog.e(TAG, "No response from client for onStopJob, '" + mRunningJob.toShortString()); - closeAndCleanupJobLocked(true /* needsReschedule */); + closeAndCleanupJobLocked(true /* needsReschedule */, "timed out while stopping"); break; case VERB_EXECUTING: // Not an error - client ran out of time. Slog.i(TAG, "Client timed out while executing (no jobFinished received)." + " sending onStop. " + mRunningJob.toShortString()); mParams.setStopReason(JobParameters.REASON_TIMEOUT); - sendStopMessageLocked(); + sendStopMessageLocked("timeout while executing"); break; default: Slog.e(TAG, "Handling timeout for an invalid job state: " + mRunningJob.toShortString() + ", dropping."); - closeAndCleanupJobLocked(false /* needsReschedule */); + closeAndCleanupJobLocked(false /* needsReschedule */, "invalid timeout"); } } @@ -606,11 +627,11 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING -> * VERB_STOPPING. */ - private void sendStopMessageLocked() { + private void sendStopMessageLocked(String reason) { removeOpTimeOutLocked(); if (mVerb != VERB_EXECUTING) { Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob); - closeAndCleanupJobLocked(false /* reschedule */); + closeAndCleanupJobLocked(false /* reschedule */, reason); return; } try { @@ -620,7 +641,7 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne } catch (RemoteException e) { Slog.e(TAG, "Error sending onStopJob to client.", e); // The job's host app apparently crashed during the job, so we should reschedule. - closeAndCleanupJobLocked(true /* reschedule */); + closeAndCleanupJobLocked(true /* reschedule */, "host crashed when trying to stop"); } } @@ -630,11 +651,12 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne * or from acknowledging the stop message we sent. Either way, we're done tracking it and * we want to clean up internally. */ - private void closeAndCleanupJobLocked(boolean reschedule) { + private void closeAndCleanupJobLocked(boolean reschedule, String reason) { final JobStatus completedJob; if (mVerb == VERB_FINISHED) { return; } + applyStoppedReasonLocked(reason); completedJob = mRunningJob; mJobPackageTracker.noteInactive(completedJob); try { @@ -658,6 +680,13 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne mCompletedListener.onJobCompletedLocked(completedJob, reschedule); } + private void applyStoppedReasonLocked(String reason) { + if (reason != null && mStoppedReason == null) { + mStoppedReason = reason; + mStoppedTime = SystemClock.elapsedRealtime(); + } + } + /** * Called when sending a message to the client, over whose execution we have no control. If * we haven't received a response in a certain amount of time, we want to give up and carry