diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index 007ea88d676cc..3cb59f27bfafe 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -111,8 +111,11 @@ public class JobInfo implements Parcelable { /* Minimum flex for a periodic job, in milliseconds. */ private static final long MIN_FLEX_MILLIS = 5 * 60 * 1000L; // 5 minutes - /* Minimum backoff interval for a job, in milliseconds */ - private static final long MIN_BACKOFF_MILLIS = 10 * 1000L; // 10 seconds + /** + * Minimum backoff interval for a job, in milliseconds + * @hide + */ + public static final long MIN_BACKOFF_MILLIS = 10 * 1000L; // 10 seconds /** * Query the minimum interval allowed for periodic scheduled jobs. Attempting @@ -431,7 +434,7 @@ public class JobInfo implements Parcelable { /** * The amount of time the JobScheduler will wait before rescheduling a failed job. This value * will be increased depending on the backoff policy specified at job creation time. Defaults - * to 5 seconds. + * to 30 seconds, minimum is currently 10 seconds. */ public long getInitialBackoffMillis() { final long minBackoff = getMinBackoffMillis(); diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java index c3d6606089cb3..f9077218dfe53 100644 --- a/core/java/android/content/BroadcastReceiver.java +++ b/core/java/android/content/BroadcastReceiver.java @@ -330,10 +330,32 @@ public abstract class BroadcastReceiver { * This can be called by an application in {@link #onReceive} to allow * it to keep the broadcast active after returning from that function. * This does not change the expectation of being relatively - * responsive to the broadcast (finishing it within 10s), but does allow + * responsive to the broadcast, but does allow * the implementation to move work related to it over to another thread * to avoid glitching the main UI thread due to disk IO. * + *

As a general rule, broadcast receivers are allowed to run for up to 10 seconds + * before they system will consider them non-responsive and ANR the app. Since these usually + * execute on the app's main thread, they are already bound by the ~5 second time limit + * of various operations that can happen there (not to mention just avoiding UI jank), so + * the receive limit is generally not of concern. However, once you use {@goAsync}, though + * able to be off the main thread, the broadcast execution limit still applies, and that + * includes the time spent between calling this method and ultimately + * {@link PendingResult#finish() PendingResult.finish()}.

+ * + *

If you are taking advantage of this method to have more time to execute, it is useful + * to know that the available time can be longer in certain situations. In particular, if + * the broadcast you are receiving is not a foreground broadcast (that is, the sender has not + * used {@link Intent#FLAG_RECEIVER_FOREGROUND}), then more time is allowed for the receivers + * to run, allowing them to execute for 30 seconds or even a bit more. This is something that + * receivers should rarely take advantage of (long work should be punted to another system + * facility such as {@link android.app.job.JobScheduler}, {@link android.app.Service}, or + * see especially {@link android.support.v4.app.JobIntentService}), but can be useful in + * certain rare cases where it is necessary to do some work as soon as the broadcast is + * delivered. Keep in mind that the work you do here will block further broadcasts until + * it completes, so taking advantage of this at all excessively can be counter-productive + * and cause later events to be received more slowly.

+ * * @return Returns a {@link PendingResult} representing the result of * the active broadcast. The BroadcastRecord itself is no longer active; * all data and other interaction must go through {@link PendingResult} diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ee57952685098..652a3c42b1fec 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5376,8 +5376,9 @@ public class ActivityManagerService extends IActivityManager.Stub boolean doLowMem = app.instr == null; boolean doOomAdj = doLowMem; if (!app.killedByAm) { - Slog.i(TAG, "Process " + app.processName + " (pid " + pid - + ") has died"); + Slog.i(TAG, "Process " + app.processName + " (pid " + pid + ") has died: " + + ProcessList.makeOomAdjString(app.setAdj) + + ProcessList.makeProcStateString(app.setProcState)); mAllowLowerMemLevel = true; } else { // Note that we always want to do oom adj to update our state with the @@ -5385,7 +5386,8 @@ public class ActivityManagerService extends IActivityManager.Stub mAllowLowerMemLevel = false; doLowMem = false; } - EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName); + EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName, + app.setAdj, app.setProcState); if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP, "Dying app: " + app + ", pid: " + pid + ", thread: " + thread.asBinder()); handleAppDiedLocked(app, false, true); diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index 372ab6ba8aebc..b2d3137bb3bc1 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -34,7 +34,7 @@ option java_package com.android.server.am # Application process bound to work 30010 am_proc_bound (User|1|5),(PID|1|5),(Process Name|3) # Application process died -30011 am_proc_died (User|1|5),(PID|1|5),(Process Name|3) +30011 am_proc_died (User|1|5),(PID|1|5),(Process Name|3),(OomAdj|1|5),(ProcState|1|5) # The Activity Manager failed to pause the given activity. 30012 am_failed_to_pause (User|1|5),(Token|1|5),(Wanting to pause|3),(Currently pausing|3) # Attempting to pause the current activity diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index e2c1274a4be6a..c08f866086568 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -218,6 +218,11 @@ public final class JobSchedulerService extends com.android.server.SystemService private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count"; private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count"; private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count"; + private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT + = "max_standard_reschedule_count"; + private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count"; + private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time"; + private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time"; private static final int DEFAULT_MIN_IDLE_COUNT = 1; private static final int DEFAULT_MIN_CHARGING_COUNT = 1; @@ -233,6 +238,10 @@ public final class JobSchedulerService extends com.android.server.SystemService private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4; private static final int DEFAULT_BG_LOW_JOB_COUNT = 1; private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1; + private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE; + private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE; + private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS; + private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS; /** * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things @@ -303,6 +312,24 @@ public final class JobSchedulerService extends com.android.server.SystemService * memory state. */ int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT; + /** + * The maximum number of times we allow a job to have itself rescheduled before + * giving up on it, for standard jobs. + */ + int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT; + /** + * The maximum number of times we allow a job to have itself rescheduled before + * giving up on it, for jobs that are executing work. + */ + int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT; + /** + * The minimum backoff time to allow for linear backoff. + */ + long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME; + /** + * The minimum backoff time to allow for exponential backoff. + */ + long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME; private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); @@ -374,6 +401,14 @@ public final class JobSchedulerService extends com.android.server.SystemService if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; } + MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT, + DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT); + MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT, + DEFAULT_MAX_WORK_RESCHEDULE_COUNT); + MIN_LINEAR_BACKOFF_TIME = mParser.getLong(KEY_MIN_LINEAR_BACKOFF_TIME, + DEFAULT_MIN_LINEAR_BACKOFF_TIME); + MIN_EXP_BACKOFF_TIME = mParser.getLong(KEY_MIN_EXP_BACKOFF_TIME, + DEFAULT_MIN_EXP_BACKOFF_TIME); } } @@ -421,11 +456,38 @@ public final class JobSchedulerService extends com.android.server.SystemService pw.print(" "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("="); pw.print(BG_CRITICAL_JOB_COUNT); pw.println(); + + pw.print(" "); pw.print(KEY_MAX_STANDARD_RESCHEDULE_COUNT); pw.print("="); + pw.print(MAX_STANDARD_RESCHEDULE_COUNT); pw.println(); + + pw.print(" "); pw.print(KEY_MAX_WORK_RESCHEDULE_COUNT); pw.print("="); + pw.print(MAX_WORK_RESCHEDULE_COUNT); pw.println(); + + pw.print(" "); pw.print(KEY_MIN_LINEAR_BACKOFF_TIME); pw.print("="); + pw.print(MIN_LINEAR_BACKOFF_TIME); pw.println(); + + pw.print(" "); pw.print(KEY_MIN_EXP_BACKOFF_TIME); pw.print("="); + pw.print(MIN_EXP_BACKOFF_TIME); pw.println(); } } final Constants mConstants; + static final Comparator mEnqueueTimeComparator = (o1, o2) -> { + if (o1.enqueueTime < o2.enqueueTime) { + return -1; + } + return o1.enqueueTime > o2.enqueueTime ? 1 : 0; + }; + + static void addOrderedItem(ArrayList array, T newItem, Comparator comparator) { + int where = Collections.binarySearch(array, newItem, comparator); + if (where < 0) { + where = ~where; + } + array.add(where, newItem); + } + /** * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we * still clean up. On reinstall the package will have a new uid. @@ -647,7 +709,7 @@ public final class JobSchedulerService extends com.android.server.SystemService // This is a new job, we can just immediately put it on the pending // list and try to run it. mJobPackageTracker.notePending(jobStatus); - mPendingJobs.add(jobStatus); + addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator); maybeRunPendingJobsLocked(); } } @@ -771,7 +833,7 @@ public final class JobSchedulerService extends com.android.server.SystemService // except those using the idle exemption flag. for (int i=0; i mConstants.MAX_WORK_RESCHEDULE_COUNT) { + Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #" + + backoffAttempts + " > work limit " + + mConstants.MAX_STANDARD_RESCHEDULE_COUNT); + return null; + } + } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) { + Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #" + + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT); + return null; + } + switch (job.getBackoffPolicy()) { - case JobInfo.BACKOFF_POLICY_LINEAR: - delayMillis = initialBackoffMillis * backoffAttempts; - break; + case JobInfo.BACKOFF_POLICY_LINEAR: { + long backoff = initialBackoffMillis; + if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) { + backoff = mConstants.MIN_LINEAR_BACKOFF_TIME; + } + delayMillis = backoff * backoffAttempts; + } break; default: if (DEBUG) { Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential."); } - case JobInfo.BACKOFF_POLICY_EXPONENTIAL: - delayMillis = - (long) Math.scalb(initialBackoffMillis, backoffAttempts - 1); - break; + case JobInfo.BACKOFF_POLICY_EXPONENTIAL: { + long backoff = initialBackoffMillis; + if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) { + backoff = mConstants.MIN_EXP_BACKOFF_TIME; + } + delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1); + } break; } delayMillis = Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS); @@ -1163,7 +1242,7 @@ public final class JobSchedulerService extends com.android.server.SystemService // state is such that all ready jobs should be run immediately. if (runNow != null && isReadyToBeExecutedLocked(runNow)) { mJobPackageTracker.notePending(runNow); - mPendingJobs.add(runNow); + addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator); } else { queueReadyJobsForExecutionLocked(); } @@ -1237,6 +1316,9 @@ public final class JobSchedulerService extends com.android.server.SystemService if (newReadyJobs != null) { noteJobsPending(newReadyJobs); mPendingJobs.addAll(newReadyJobs); + if (mPendingJobs.size() > 1) { + mPendingJobs.sort(mEnqueueTimeComparator); + } } newReadyJobs = null; } @@ -1326,6 +1408,9 @@ public final class JobSchedulerService extends com.android.server.SystemService } noteJobsPending(runnableJobs); mPendingJobs.addAll(runnableJobs); + if (mPendingJobs.size() > 1) { + mPendingJobs.sort(mEnqueueTimeComparator); + } } else { if (DEBUG) { Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything."); @@ -1515,7 +1600,7 @@ public final class JobSchedulerService extends com.android.server.SystemService int numForeground = 0; for (int i=0; i= JobInfo.PRIORITY_TOP_APP) { @@ -1591,10 +1676,10 @@ public final class JobSchedulerService extends com.android.server.SystemService for (int i=0; i 0) || hasExecutingWorkLocked(); + } + public boolean hasExecutingWorkLocked() { return executingWork != null && executingWork.size() > 0; } @@ -744,10 +747,11 @@ public final class JobStatus { sb.append(getSourceUid()); if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME || latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) { + long now = SystemClock.elapsedRealtime(); sb.append(" TIME="); - sb.append(formatRunTime(earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME)); - sb.append("-"); - sb.append(formatRunTime(latestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME)); + formatRunTime(sb, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, now); + sb.append(":"); + formatRunTime(sb, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, now); } if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) { sb.append(" NET="); @@ -789,17 +793,19 @@ public final class JobStatus { return sb.toString(); } - private String formatRunTime(long runtime, long defaultValue) { + private void formatRunTime(PrintWriter pw, long runtime, long defaultValue, long now) { if (runtime == defaultValue) { - return "none"; + pw.print("none"); } else { - long elapsedNow = SystemClock.elapsedRealtime(); - long nextRuntime = runtime - elapsedNow; - if (nextRuntime > 0) { - return DateUtils.formatElapsedTime(nextRuntime / 1000); - } else { - return "-" + DateUtils.formatElapsedTime(nextRuntime / -1000); - } + TimeUtils.formatDuration(runtime - now, pw); + } + } + + private void formatRunTime(StringBuilder sb, long runtime, long defaultValue, long now) { + if (runtime == defaultValue) { + sb.append("none"); + } else { + TimeUtils.formatDuration(runtime - now, sb); } } @@ -881,7 +887,7 @@ public final class JobStatus { } // Dumpsys infrastructure - public void dump(PrintWriter pw, String prefix, boolean full) { + public void dump(PrintWriter pw, String prefix, boolean full, long elapsedRealtimeMillis) { pw.print(prefix); UserHandle.formatUid(pw, callingUid); pw.print(" tag="); pw.println(tag); pw.print(prefix); @@ -1020,10 +1026,14 @@ public final class JobStatus { dumpJobWorkItem(pw, prefix, executingWork.get(i), i); } } - pw.print(prefix); pw.print("Earliest run time: "); - pw.println(formatRunTime(earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME)); - pw.print(prefix); pw.print("Latest run time: "); - pw.println(formatRunTime(latestRunTimeElapsedMillis, NO_LATEST_RUNTIME)); + pw.print(prefix); pw.print("Enqueue time: "); + TimeUtils.formatDuration(enqueueTime, elapsedRealtimeMillis, pw); + pw.println(); + pw.print(prefix); pw.print("Run time: earliest="); + formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, elapsedRealtimeMillis); + pw.print(", latest="); + formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, elapsedRealtimeMillis); + pw.println(); if (numFailures != 0) { pw.print(prefix); pw.print("Num failures: "); pw.println(numFailures); }