Home | History | Annotate | Download | only in job
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.server.job;
     18 
     19 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
     20 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
     21 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
     22 
     23 import android.annotation.NonNull;
     24 import android.annotation.UserIdInt;
     25 import android.app.Activity;
     26 import android.app.ActivityManager;
     27 import android.app.ActivityManagerInternal;
     28 import android.app.AlarmManager;
     29 import android.app.AppGlobals;
     30 import android.app.IUidObserver;
     31 import android.app.job.IJobScheduler;
     32 import android.app.job.JobInfo;
     33 import android.app.job.JobParameters;
     34 import android.app.job.JobProtoEnums;
     35 import android.app.job.JobScheduler;
     36 import android.app.job.JobService;
     37 import android.app.job.JobSnapshot;
     38 import android.app.job.JobWorkItem;
     39 import android.app.usage.UsageStatsManager;
     40 import android.app.usage.UsageStatsManagerInternal;
     41 import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
     42 import android.content.BroadcastReceiver;
     43 import android.content.ComponentName;
     44 import android.content.ContentResolver;
     45 import android.content.Context;
     46 import android.content.Intent;
     47 import android.content.IntentFilter;
     48 import android.content.pm.IPackageManager;
     49 import android.content.pm.PackageManager;
     50 import android.content.pm.PackageManager.NameNotFoundException;
     51 import android.content.pm.PackageManagerInternal;
     52 import android.content.pm.ParceledListSlice;
     53 import android.content.pm.ServiceInfo;
     54 import android.database.ContentObserver;
     55 import android.net.Uri;
     56 import android.os.BatteryStats;
     57 import android.os.BatteryStatsInternal;
     58 import android.os.Binder;
     59 import android.os.Handler;
     60 import android.os.IThermalService;
     61 import android.os.IThermalStatusListener;
     62 import android.os.Looper;
     63 import android.os.Message;
     64 import android.os.Process;
     65 import android.os.RemoteException;
     66 import android.os.ResultReceiver;
     67 import android.os.ServiceManager;
     68 import android.os.ShellCallback;
     69 import android.os.SystemClock;
     70 import android.os.Temperature;
     71 import android.os.UserHandle;
     72 import android.os.UserManagerInternal;
     73 import android.os.WorkSource;
     74 import android.provider.Settings;
     75 import android.text.format.DateUtils;
     76 import android.util.KeyValueListParser;
     77 import android.util.Log;
     78 import android.util.Slog;
     79 import android.util.SparseArray;
     80 import android.util.SparseIntArray;
     81 import android.util.StatsLog;
     82 import android.util.TimeUtils;
     83 import android.util.proto.ProtoOutputStream;
     84 
     85 import com.android.internal.annotations.GuardedBy;
     86 import com.android.internal.annotations.VisibleForTesting;
     87 import com.android.internal.app.IBatteryStats;
     88 import com.android.internal.util.ArrayUtils;
     89 import com.android.internal.util.DumpUtils;
     90 import com.android.internal.util.IndentingPrintWriter;
     91 import com.android.internal.util.Preconditions;
     92 import com.android.server.AppStateTracker;
     93 import com.android.server.DeviceIdleController;
     94 import com.android.server.FgThread;
     95 import com.android.server.LocalServices;
     96 import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
     97 import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
     98 import com.android.server.job.JobSchedulerServiceDumpProto.RegisteredJob;
     99 import com.android.server.job.controllers.BackgroundJobsController;
    100 import com.android.server.job.controllers.BatteryController;
    101 import com.android.server.job.controllers.ConnectivityController;
    102 import com.android.server.job.controllers.ContentObserverController;
    103 import com.android.server.job.controllers.DeviceIdleJobsController;
    104 import com.android.server.job.controllers.IdleController;
    105 import com.android.server.job.controllers.JobStatus;
    106 import com.android.server.job.controllers.QuotaController;
    107 import com.android.server.job.controllers.StateController;
    108 import com.android.server.job.controllers.StorageController;
    109 import com.android.server.job.controllers.TimeController;
    110 
    111 import libcore.util.EmptyArray;
    112 
    113 import java.io.FileDescriptor;
    114 import java.io.PrintWriter;
    115 import java.time.Clock;
    116 import java.util.ArrayList;
    117 import java.util.Arrays;
    118 import java.util.Collections;
    119 import java.util.Comparator;
    120 import java.util.HashMap;
    121 import java.util.List;
    122 import java.util.function.Consumer;
    123 import java.util.function.Predicate;
    124 
    125 /**
    126  * Responsible for taking jobs representing work to be performed by a client app, and determining
    127  * based on the criteria specified when that job should be run against the client application's
    128  * endpoint.
    129  * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
    130  * about constraints, or the state of active jobs. It receives callbacks from the various
    131  * controllers and completed jobs and operates accordingly.
    132  *
    133  * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
    134  * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
    135  * @hide
    136  */
    137 public class JobSchedulerService extends com.android.server.SystemService
    138         implements StateChangedListener, JobCompletedListener {
    139     public static final String TAG = "JobScheduler";
    140     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    141     public static final boolean DEBUG_STANDBY = DEBUG || false;
    142 
    143     /** The maximum number of concurrent jobs we run at one time. */
    144     static final int MAX_JOB_CONTEXTS_COUNT = 16;
    145     /** Enforce a per-app limit on scheduled jobs? */
    146     private static final boolean ENFORCE_MAX_JOBS = true;
    147     /** The maximum number of jobs that we allow an unprivileged app to schedule */
    148     private static final int MAX_JOBS_PER_APP = 100;
    149 
    150     @VisibleForTesting
    151     public static Clock sSystemClock = Clock.systemUTC();
    152     @VisibleForTesting
    153     public static Clock sUptimeMillisClock = SystemClock.uptimeMillisClock();
    154     @VisibleForTesting
    155     public static Clock sElapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
    156 
    157     /** Global local for all job scheduler state. */
    158     final Object mLock = new Object();
    159     /** Master list of jobs. */
    160     final JobStore mJobs;
    161     /** Tracking the standby bucket state of each app */
    162     final StandbyTracker mStandbyTracker;
    163     /** Tracking amount of time each package runs for. */
    164     final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
    165     final JobConcurrencyManager mConcurrencyManager;
    166 
    167     static final int MSG_JOB_EXPIRED = 0;
    168     static final int MSG_CHECK_JOB = 1;
    169     static final int MSG_STOP_JOB = 2;
    170     static final int MSG_CHECK_JOB_GREEDY = 3;
    171     static final int MSG_UID_STATE_CHANGED = 4;
    172     static final int MSG_UID_GONE = 5;
    173     static final int MSG_UID_ACTIVE = 6;
    174     static final int MSG_UID_IDLE = 7;
    175 
    176     /**
    177      * Track Services that have currently active or pending jobs. The index is provided by
    178      * {@link JobStatus#getServiceToken()}
    179      */
    180     final List<JobServiceContext> mActiveServices = new ArrayList<>();
    181 
    182     /** List of controllers that will notify this service of updates to jobs. */
    183     final List<StateController> mControllers;
    184     /** Need direct access to this for testing. */
    185     private final BatteryController mBatteryController;
    186     /** Need direct access to this for testing. */
    187     private final StorageController mStorageController;
    188     /** Need directly for sending uid state changes */
    189     private final DeviceIdleJobsController mDeviceIdleJobsController;
    190     /** Need directly for receiving thermal events */
    191     private IThermalService mThermalService;
    192     /** Thermal constraint. */
    193     @GuardedBy("mLock")
    194     private boolean mThermalConstraint = false;
    195 
    196     /**
    197      * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
    198      * when ready to execute them.
    199      */
    200     final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
    201 
    202     int[] mStartedUsers = EmptyArray.INT;
    203 
    204     final JobHandler mHandler;
    205     final JobSchedulerStub mJobSchedulerStub;
    206 
    207     PackageManagerInternal mLocalPM;
    208     ActivityManagerInternal mActivityManagerInternal;
    209     IBatteryStats mBatteryStats;
    210     DeviceIdleController.LocalService mLocalDeviceIdleController;
    211     AppStateTracker mAppStateTracker;
    212     final UsageStatsManagerInternal mUsageStats;
    213 
    214     /**
    215      * Set to true once we are allowed to run third party apps.
    216      */
    217     boolean mReadyToRock;
    218 
    219     /**
    220      * What we last reported to DeviceIdleController about whether we are active.
    221      */
    222     boolean mReportedActive;
    223 
    224     /**
    225      * Are we currently in device-wide standby parole?
    226      */
    227     volatile boolean mInParole;
    228 
    229     /**
    230      * A mapping of which uids are currently in the foreground to their effective priority.
    231      */
    232     final SparseIntArray mUidPriorityOverride = new SparseIntArray();
    233 
    234     /**
    235      * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
    236      */
    237     final SparseIntArray mBackingUpUids = new SparseIntArray();
    238 
    239     /**
    240      * Count standby heartbeats, and keep track of which beat each bucket's jobs will
    241      * next become runnable.  Index into this array is by normalized bucket:
    242      * { ACTIVE, WORKING, FREQUENT, RARE, NEVER }.  The ACTIVE and NEVER bucket
    243      * milestones are not updated: ACTIVE apps get jobs whenever they ask for them,
    244      * and NEVER apps don't get them at all.
    245      */
    246     final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE };
    247     long mHeartbeat = 0;
    248     long mLastHeartbeatTime = sElapsedRealtimeClock.millis();
    249 
    250     /**
    251      * Named indices into the STANDBY_BEATS array, for clarity in referring to
    252      * specific buckets' bookkeeping.
    253      */
    254     public static final int ACTIVE_INDEX = 0;
    255     public static final int WORKING_INDEX = 1;
    256     public static final int FREQUENT_INDEX = 2;
    257     public static final int RARE_INDEX = 3;
    258     public static final int NEVER_INDEX = 4;
    259 
    260     /**
    261      * Bookkeeping about when jobs last run.  We keep our own record in heartbeat time,
    262      * rather than rely on Usage Stats' timestamps, because heartbeat time can be
    263      * manipulated for testing purposes and we need job runnability to track that rather
    264      * than real time.
    265      *
    266      * Outer SparseArray slices by user handle; inner map of package name to heartbeat
    267      * is a HashMap<> rather than ArrayMap<> because we expect O(hundreds) of keys
    268      * and it will be accessed in a known-hot code path.
    269      */
    270     final SparseArray<HashMap<String, Long>> mLastJobHeartbeats = new SparseArray<>();
    271 
    272     static final String HEARTBEAT_TAG = "*job.heartbeat*";
    273     final HeartbeatAlarmListener mHeartbeatAlarm = new HeartbeatAlarmListener();
    274 
    275     // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
    276 
    277     private class ConstantsObserver extends ContentObserver {
    278         private ContentResolver mResolver;
    279 
    280         public ConstantsObserver(Handler handler) {
    281             super(handler);
    282         }
    283 
    284         public void start(ContentResolver resolver) {
    285             mResolver = resolver;
    286             mResolver.registerContentObserver(Settings.Global.getUriFor(
    287                     Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
    288             updateConstants();
    289         }
    290 
    291         @Override
    292         public void onChange(boolean selfChange, Uri uri) {
    293             updateConstants();
    294         }
    295 
    296         private void updateConstants() {
    297             synchronized (mLock) {
    298                 try {
    299                     mConstants.updateConstantsLocked(Settings.Global.getString(mResolver,
    300                             Settings.Global.JOB_SCHEDULER_CONSTANTS));
    301                     for (int controller = 0; controller < mControllers.size(); controller++) {
    302                         final StateController sc = mControllers.get(controller);
    303                         sc.onConstantsUpdatedLocked();
    304                     }
    305                 } catch (IllegalArgumentException e) {
    306                     // Failed to parse the settings string, log this and move on
    307                     // with defaults.
    308                     Slog.e(TAG, "Bad jobscheduler settings", e);
    309                 }
    310             }
    311 
    312             if (mConstants.USE_HEARTBEATS) {
    313                 // Reset the heartbeat alarm based on the new heartbeat duration
    314                 setNextHeartbeatAlarm();
    315             }
    316         }
    317     }
    318 
    319     /**
    320      *  Thermal event received from Thermal Service
    321      */
    322     private final class ThermalStatusListener extends IThermalStatusListener.Stub {
    323         @Override public void onStatusChange(int status) {
    324             // Throttle for Temperature.THROTTLING_SEVERE and above
    325             synchronized (mLock) {
    326                 mThermalConstraint = status >= Temperature.THROTTLING_SEVERE;
    327             }
    328             onControllerStateChanged();
    329         }
    330     }
    331 
    332     static class MaxJobCounts {
    333         private final KeyValueListParser.IntValue mTotal;
    334         private final KeyValueListParser.IntValue mMaxBg;
    335         private final KeyValueListParser.IntValue mMinBg;
    336 
    337         MaxJobCounts(int totalDefault, String totalKey,
    338                 int maxBgDefault, String maxBgKey, int minBgDefault, String minBgKey) {
    339             mTotal = new KeyValueListParser.IntValue(totalKey, totalDefault);
    340             mMaxBg = new KeyValueListParser.IntValue(maxBgKey, maxBgDefault);
    341             mMinBg = new KeyValueListParser.IntValue(minBgKey, minBgDefault);
    342         }
    343 
    344         public void parse(KeyValueListParser parser) {
    345             mTotal.parse(parser);
    346             mMaxBg.parse(parser);
    347             mMinBg.parse(parser);
    348 
    349             if (mTotal.getValue() < 1) {
    350                 mTotal.setValue(1);
    351             } else if (mTotal.getValue() > MAX_JOB_CONTEXTS_COUNT) {
    352                 mTotal.setValue(MAX_JOB_CONTEXTS_COUNT);
    353             }
    354 
    355             if (mMaxBg.getValue() < 1) {
    356                 mMaxBg.setValue(1);
    357             } else if (mMaxBg.getValue() > mTotal.getValue()) {
    358                 mMaxBg.setValue(mTotal.getValue());
    359             }
    360             if (mMinBg.getValue() < 0) {
    361                 mMinBg.setValue(0);
    362             } else {
    363                 if (mMinBg.getValue() > mMaxBg.getValue()) {
    364                     mMinBg.setValue(mMaxBg.getValue());
    365                 }
    366                 if (mMinBg.getValue() >= mTotal.getValue()) {
    367                     mMinBg.setValue(mTotal.getValue() - 1);
    368                 }
    369             }
    370         }
    371 
    372         /** Total number of jobs to run simultaneously. */
    373         public int getMaxTotal() {
    374             return mTotal.getValue();
    375         }
    376 
    377         /** Max number of BG (== owned by non-TOP apps) jobs to run simultaneously. */
    378         public int getMaxBg() {
    379             return mMaxBg.getValue();
    380         }
    381 
    382         /**
    383          * We try to run at least this many BG (== owned by non-TOP apps) jobs, when there are any
    384          * pending, rather than always running the TOTAL number of FG jobs.
    385          */
    386         public int getMinBg() {
    387             return mMinBg.getValue();
    388         }
    389 
    390         public void dump(PrintWriter pw, String prefix) {
    391             mTotal.dump(pw, prefix);
    392             mMaxBg.dump(pw, prefix);
    393             mMinBg.dump(pw, prefix);
    394         }
    395 
    396         public void dumpProto(ProtoOutputStream proto, long fieldId) {
    397             final long token = proto.start(fieldId);
    398             mTotal.dumpProto(proto, MaxJobCountsProto.TOTAL_JOBS);
    399             mMaxBg.dumpProto(proto, MaxJobCountsProto.MAX_BG);
    400             mMinBg.dumpProto(proto, MaxJobCountsProto.MIN_BG);
    401             proto.end(token);
    402         }
    403     }
    404 
    405     /** {@link MaxJobCounts} for each memory trim level. */
    406     static class MaxJobCountsPerMemoryTrimLevel {
    407         public final MaxJobCounts normal;
    408         public final MaxJobCounts moderate;
    409         public final MaxJobCounts low;
    410         public final MaxJobCounts critical;
    411 
    412         MaxJobCountsPerMemoryTrimLevel(
    413                 MaxJobCounts normal,
    414                 MaxJobCounts moderate, MaxJobCounts low,
    415                 MaxJobCounts critical) {
    416             this.normal = normal;
    417             this.moderate = moderate;
    418             this.low = low;
    419             this.critical = critical;
    420         }
    421 
    422         public void dumpProto(ProtoOutputStream proto, long fieldId) {
    423             final long token = proto.start(fieldId);
    424             normal.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.NORMAL);
    425             moderate.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.MODERATE);
    426             low.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.LOW);
    427             critical.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.CRITICAL);
    428             proto.end(token);
    429         }
    430     }
    431 
    432     /**
    433      * All times are in milliseconds. These constants are kept synchronized with the system
    434      * global Settings. Any access to this class or its fields should be done while
    435      * holding the JobSchedulerService.mLock lock.
    436      */
    437     public static class Constants {
    438         // Key names stored in the settings value.
    439         private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
    440         private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
    441         private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
    442         private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
    443         private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
    444         private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
    445         private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
    446         private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
    447         private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
    448 
    449         // The following values used to be used on P and below. Do not reuse them.
    450         private static final String DEPRECATED_KEY_FG_JOB_COUNT = "fg_job_count";
    451         private static final String DEPRECATED_KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
    452         private static final String DEPRECATED_KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
    453         private static final String DEPRECATED_KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
    454         private static final String DEPRECATED_KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
    455 
    456         private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
    457                 = "max_standard_reschedule_count";
    458         private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
    459         private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
    460         private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
    461         private static final String KEY_STANDBY_HEARTBEAT_TIME = "standby_heartbeat_time";
    462         private static final String KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
    463         private static final String KEY_STANDBY_FREQUENT_BEATS = "standby_frequent_beats";
    464         private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
    465         private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
    466         private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
    467         private static final String KEY_USE_HEARTBEATS = "use_heartbeats";
    468 
    469         private static final int DEFAULT_MIN_IDLE_COUNT = 1;
    470         private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
    471         private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
    472         private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
    473         private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
    474         private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
    475         private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
    476         private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
    477         private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
    478         private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
    479         private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
    480         private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
    481         private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
    482         private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L;
    483         private static final int DEFAULT_STANDBY_WORKING_BEATS = 11;  // ~ 2 hours, with 11min beats
    484         private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours
    485         private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
    486         private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
    487         private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
    488         private static final boolean DEFAULT_USE_HEARTBEATS = false;
    489 
    490         /**
    491          * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
    492          * early.
    493          */
    494         int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
    495         /**
    496          * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
    497          * things early.
    498          */
    499         int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
    500         /**
    501          * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
    502          * schedule things early.
    503          */
    504         int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
    505         /**
    506          * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
    507          * schedule things early.
    508          */
    509         int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
    510         /**
    511          * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
    512          * things early.  1 == Run connectivity jobs as soon as ready.
    513          */
    514         int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
    515         /**
    516          * Minimum # of content trigger jobs that must be ready in order to force the JMS to
    517          * schedule things early.
    518          */
    519         int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
    520         /**
    521          * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
    522          * running some work early.  This (and thus the other min counts) is now set to 1, to
    523          * prevent any batching at this level.  Since we now do batching through doze, that is
    524          * a much better mechanism.
    525          */
    526         int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
    527         /**
    528          * This is the job execution factor that is considered to be heavy use of the system.
    529          */
    530         float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
    531         /**
    532          * This is the job execution factor that is considered to be moderate use of the system.
    533          */
    534         float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
    535 
    536         // Max job counts for screen on / off, for each memory trim level.
    537         final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_ON =
    538                 new MaxJobCountsPerMemoryTrimLevel(
    539                         new MaxJobCounts(
    540                                 8, "max_job_total_on_normal",
    541                                 6, "max_job_max_bg_on_normal",
    542                                 2, "max_job_min_bg_on_normal"),
    543                         new MaxJobCounts(
    544                                 8, "max_job_total_on_moderate",
    545                                 4, "max_job_max_bg_on_moderate",
    546                                 2, "max_job_min_bg_on_moderate"),
    547                         new MaxJobCounts(
    548                                 5, "max_job_total_on_low",
    549                                 1, "max_job_max_bg_on_low",
    550                                 1, "max_job_min_bg_on_low"),
    551                         new MaxJobCounts(
    552                                 5, "max_job_total_on_critical",
    553                                 1, "max_job_max_bg_on_critical",
    554                                 1, "max_job_min_bg_on_critical"));
    555 
    556         final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_OFF =
    557                 new MaxJobCountsPerMemoryTrimLevel(
    558                         new MaxJobCounts(
    559                                 10, "max_job_total_off_normal",
    560                                 6, "max_job_max_bg_off_normal",
    561                                 2, "max_job_min_bg_off_normal"),
    562                         new MaxJobCounts(
    563                                 10, "max_job_total_off_moderate",
    564                                 4, "max_job_max_bg_off_moderate",
    565                                 2, "max_job_min_bg_off_moderate"),
    566                         new MaxJobCounts(
    567                                 5, "max_job_total_off_low",
    568                                 1, "max_job_max_bg_off_low",
    569                                 1, "max_job_min_bg_off_low"),
    570                         new MaxJobCounts(
    571                                 5, "max_job_total_off_critical",
    572                                 1, "max_job_max_bg_off_critical",
    573                                 1, "max_job_min_bg_off_critical"));
    574 
    575 
    576         /** Wait for this long after screen off before increasing the job concurrency. */
    577         final KeyValueListParser.IntValue SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS =
    578                 new KeyValueListParser.IntValue(
    579                         "screen_off_job_concurrency_increase_delay_ms", 30_000);
    580 
    581         /**
    582          * The maximum number of times we allow a job to have itself rescheduled before
    583          * giving up on it, for standard jobs.
    584          */
    585         int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
    586         /**
    587          * The maximum number of times we allow a job to have itself rescheduled before
    588          * giving up on it, for jobs that are executing work.
    589          */
    590         int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
    591         /**
    592          * The minimum backoff time to allow for linear backoff.
    593          */
    594         long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
    595         /**
    596          * The minimum backoff time to allow for exponential backoff.
    597          */
    598         long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
    599         /**
    600          * How often we recalculate runnability based on apps' standby bucket assignment.
    601          * This should be prime relative to common time interval lengths such as a quarter-
    602          * hour or day, so that the heartbeat drifts relative to wall-clock milestones.
    603          */
    604         long STANDBY_HEARTBEAT_TIME = DEFAULT_STANDBY_HEARTBEAT_TIME;
    605         /**
    606          * Mapping: standby bucket -> number of heartbeats between each sweep of that
    607          * bucket's jobs.
    608          *
    609          * Bucket assignments as recorded in the JobStatus objects are normalized to be
    610          * indices into this array, rather than the raw constants used
    611          * by AppIdleHistory.
    612          */
    613         final int[] STANDBY_BEATS = {
    614                 0,
    615                 DEFAULT_STANDBY_WORKING_BEATS,
    616                 DEFAULT_STANDBY_FREQUENT_BEATS,
    617                 DEFAULT_STANDBY_RARE_BEATS
    618         };
    619         /**
    620          * The fraction of a job's running window that must pass before we
    621          * consider running it when the network is congested.
    622          */
    623         public float CONN_CONGESTION_DELAY_FRAC = DEFAULT_CONN_CONGESTION_DELAY_FRAC;
    624         /**
    625          * The fraction of a prefetch job's running window that must pass before
    626          * we consider matching it against a metered network.
    627          */
    628         public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC;
    629         /**
    630          * Whether to use heartbeats or rolling window for quota management. True will use
    631          * heartbeats, false will use a rolling window.
    632          */
    633         public boolean USE_HEARTBEATS = DEFAULT_USE_HEARTBEATS;
    634 
    635         private final KeyValueListParser mParser = new KeyValueListParser(',');
    636 
    637         void updateConstantsLocked(String value) {
    638             try {
    639                 mParser.setString(value);
    640             } catch (Exception e) {
    641                 // Failed to parse the settings string, log this and move on
    642                 // with defaults.
    643                 Slog.e(TAG, "Bad jobscheduler settings", e);
    644             }
    645 
    646             MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
    647                     DEFAULT_MIN_IDLE_COUNT);
    648             MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
    649                     DEFAULT_MIN_CHARGING_COUNT);
    650             MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
    651                     DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
    652             MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
    653                     DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
    654             MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
    655                     DEFAULT_MIN_CONNECTIVITY_COUNT);
    656             MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
    657                     DEFAULT_MIN_CONTENT_COUNT);
    658             MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
    659                     DEFAULT_MIN_READY_JOBS_COUNT);
    660             HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
    661                     DEFAULT_HEAVY_USE_FACTOR);
    662             MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
    663                     DEFAULT_MODERATE_USE_FACTOR);
    664 
    665             MAX_JOB_COUNTS_SCREEN_ON.normal.parse(mParser);
    666             MAX_JOB_COUNTS_SCREEN_ON.moderate.parse(mParser);
    667             MAX_JOB_COUNTS_SCREEN_ON.low.parse(mParser);
    668             MAX_JOB_COUNTS_SCREEN_ON.critical.parse(mParser);
    669 
    670             MAX_JOB_COUNTS_SCREEN_OFF.normal.parse(mParser);
    671             MAX_JOB_COUNTS_SCREEN_OFF.moderate.parse(mParser);
    672             MAX_JOB_COUNTS_SCREEN_OFF.low.parse(mParser);
    673             MAX_JOB_COUNTS_SCREEN_OFF.critical.parse(mParser);
    674 
    675             SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.parse(mParser);
    676 
    677             MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
    678                     DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
    679             MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
    680                     DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
    681             MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
    682                     DEFAULT_MIN_LINEAR_BACKOFF_TIME);
    683             MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
    684                     DEFAULT_MIN_EXP_BACKOFF_TIME);
    685             STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME,
    686                     DEFAULT_STANDBY_HEARTBEAT_TIME);
    687             STANDBY_BEATS[WORKING_INDEX] = mParser.getInt(KEY_STANDBY_WORKING_BEATS,
    688                     DEFAULT_STANDBY_WORKING_BEATS);
    689             STANDBY_BEATS[FREQUENT_INDEX] = mParser.getInt(KEY_STANDBY_FREQUENT_BEATS,
    690                     DEFAULT_STANDBY_FREQUENT_BEATS);
    691             STANDBY_BEATS[RARE_INDEX] = mParser.getInt(KEY_STANDBY_RARE_BEATS,
    692                     DEFAULT_STANDBY_RARE_BEATS);
    693             CONN_CONGESTION_DELAY_FRAC = mParser.getFloat(KEY_CONN_CONGESTION_DELAY_FRAC,
    694                     DEFAULT_CONN_CONGESTION_DELAY_FRAC);
    695             CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
    696                     DEFAULT_CONN_PREFETCH_RELAX_FRAC);
    697             USE_HEARTBEATS = mParser.getBoolean(KEY_USE_HEARTBEATS, DEFAULT_USE_HEARTBEATS);
    698         }
    699 
    700         void dump(IndentingPrintWriter pw) {
    701             pw.println("Settings:");
    702             pw.increaseIndent();
    703             pw.printPair(KEY_MIN_IDLE_COUNT, MIN_IDLE_COUNT).println();
    704             pw.printPair(KEY_MIN_CHARGING_COUNT, MIN_CHARGING_COUNT).println();
    705             pw.printPair(KEY_MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT).println();
    706             pw.printPair(KEY_MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT).println();
    707             pw.printPair(KEY_MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT).println();
    708             pw.printPair(KEY_MIN_CONTENT_COUNT, MIN_CONTENT_COUNT).println();
    709             pw.printPair(KEY_MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT).println();
    710             pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println();
    711             pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println();
    712 
    713             MAX_JOB_COUNTS_SCREEN_ON.normal.dump(pw, "");
    714             MAX_JOB_COUNTS_SCREEN_ON.moderate.dump(pw, "");
    715             MAX_JOB_COUNTS_SCREEN_ON.low.dump(pw, "");
    716             MAX_JOB_COUNTS_SCREEN_ON.critical.dump(pw, "");
    717 
    718             MAX_JOB_COUNTS_SCREEN_OFF.normal.dump(pw, "");
    719             MAX_JOB_COUNTS_SCREEN_OFF.moderate.dump(pw, "");
    720             MAX_JOB_COUNTS_SCREEN_OFF.low.dump(pw, "");
    721             MAX_JOB_COUNTS_SCREEN_OFF.critical.dump(pw, "");
    722 
    723             SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dump(pw, "");
    724 
    725             pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println();
    726             pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
    727             pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
    728             pw.printPair(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println();
    729             pw.printPair(KEY_STANDBY_HEARTBEAT_TIME, STANDBY_HEARTBEAT_TIME).println();
    730             pw.print("standby_beats={");
    731             pw.print(STANDBY_BEATS[0]);
    732             for (int i = 1; i < STANDBY_BEATS.length; i++) {
    733                 pw.print(", ");
    734                 pw.print(STANDBY_BEATS[i]);
    735             }
    736             pw.println('}');
    737             pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
    738             pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
    739             pw.printPair(KEY_USE_HEARTBEATS, USE_HEARTBEATS).println();
    740 
    741             pw.decreaseIndent();
    742         }
    743 
    744         void dump(ProtoOutputStream proto) {
    745             proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT);
    746             proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT);
    747             proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT);
    748             proto.write(ConstantsProto.MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT);
    749             proto.write(ConstantsProto.MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT);
    750             proto.write(ConstantsProto.MIN_CONTENT_COUNT, MIN_CONTENT_COUNT);
    751             proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT);
    752             proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
    753             proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
    754 
    755             MAX_JOB_COUNTS_SCREEN_ON.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_ON);
    756             MAX_JOB_COUNTS_SCREEN_OFF.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_OFF);
    757 
    758             SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dumpProto(proto,
    759                     ConstantsProto.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
    760 
    761             proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
    762             proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
    763             proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
    764             proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
    765             proto.write(ConstantsProto.STANDBY_HEARTBEAT_TIME_MS, STANDBY_HEARTBEAT_TIME);
    766             for (int period : STANDBY_BEATS) {
    767                 proto.write(ConstantsProto.STANDBY_BEATS, period);
    768             }
    769             proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
    770             proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
    771             proto.write(ConstantsProto.USE_HEARTBEATS, USE_HEARTBEATS);
    772         }
    773     }
    774 
    775     final Constants mConstants;
    776     final ConstantsObserver mConstantsObserver;
    777 
    778     static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
    779         if (o1.enqueueTime < o2.enqueueTime) {
    780             return -1;
    781         }
    782         return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
    783     };
    784 
    785     static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
    786         int where = Collections.binarySearch(array, newItem, comparator);
    787         if (where < 0) {
    788             where = ~where;
    789         }
    790         array.add(where, newItem);
    791     }
    792 
    793     /**
    794      * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
    795      * still clean up. On reinstall the package will have a new uid.
    796      */
    797     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    798         @Override
    799         public void onReceive(Context context, Intent intent) {
    800             final String action = intent.getAction();
    801             if (DEBUG) {
    802                 Slog.d(TAG, "Receieved: " + action);
    803             }
    804             final String pkgName = getPackageName(intent);
    805             final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
    806 
    807             if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
    808                 // Purge the app's jobs if the whole package was just disabled.  When this is
    809                 // the case the component name will be a bare package name.
    810                 if (pkgName != null && pkgUid != -1) {
    811                     final String[] changedComponents = intent.getStringArrayExtra(
    812                             Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
    813                     if (changedComponents != null) {
    814                         for (String component : changedComponents) {
    815                             if (component.equals(pkgName)) {
    816                                 if (DEBUG) {
    817                                     Slog.d(TAG, "Package state change: " + pkgName);
    818                                 }
    819                                 try {
    820                                     final int userId = UserHandle.getUserId(pkgUid);
    821                                     IPackageManager pm = AppGlobals.getPackageManager();
    822                                     final int state = pm.getApplicationEnabledSetting(pkgName, userId);
    823                                     if (state == COMPONENT_ENABLED_STATE_DISABLED
    824                                             || state ==  COMPONENT_ENABLED_STATE_DISABLED_USER) {
    825                                         if (DEBUG) {
    826                                             Slog.d(TAG, "Removing jobs for package " + pkgName
    827                                                     + " in user " + userId);
    828                                         }
    829                                         cancelJobsForPackageAndUid(pkgName, pkgUid,
    830                                                 "app disabled");
    831                                     }
    832                                 } catch (RemoteException|IllegalArgumentException e) {
    833                                     /*
    834                                      * IllegalArgumentException means that the package doesn't exist.
    835                                      * This arises when PACKAGE_CHANGED broadcast delivery has lagged
    836                                      * behind outright uninstall, so by the time we try to act it's gone.
    837                                      * We don't need to act on this PACKAGE_CHANGED when this happens;
    838                                      * we'll get a PACKAGE_REMOVED later and clean up then.
    839                                      *
    840                                      * RemoteException can't actually happen; the package manager is
    841                                      * running in this same process.
    842                                      */
    843                                 }
    844                                 break;
    845                             }
    846                         }
    847                         if (DEBUG) {
    848                             Slog.d(TAG, "Something in " + pkgName
    849                                     + " changed. Reevaluating controller states.");
    850                         }
    851                         synchronized (mLock) {
    852                             for (int c = mControllers.size() - 1; c >= 0; --c) {
    853                                 mControllers.get(c).reevaluateStateLocked(pkgUid);
    854                             }
    855                         }
    856                     }
    857                 } else {
    858                     Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
    859                 }
    860             } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
    861                 // If this is an outright uninstall rather than the first half of an
    862                 // app update sequence, cancel the jobs associated with the app.
    863                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
    864                     int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
    865                     if (DEBUG) {
    866                         Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
    867                     }
    868                     cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
    869                     synchronized (mLock) {
    870                         for (int c = 0; c < mControllers.size(); ++c) {
    871                             mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
    872                         }
    873                     }
    874                 }
    875             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
    876                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
    877                 if (DEBUG) {
    878                     Slog.d(TAG, "Removing jobs for user: " + userId);
    879                 }
    880                 cancelJobsForUser(userId);
    881                 synchronized (mLock) {
    882                     for (int c = 0; c < mControllers.size(); ++c) {
    883                         mControllers.get(c).onUserRemovedLocked(userId);
    884                     }
    885                 }
    886             } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
    887                 // Has this package scheduled any jobs, such that we will take action
    888                 // if it were to be force-stopped?
    889                 if (pkgUid != -1) {
    890                     List<JobStatus> jobsForUid;
    891                     synchronized (mLock) {
    892                         jobsForUid = mJobs.getJobsByUid(pkgUid);
    893                     }
    894                     for (int i = jobsForUid.size() - 1; i >= 0; i--) {
    895                         if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
    896                             if (DEBUG) {
    897                                 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
    898                                         + pkgUid + " has jobs");
    899                             }
    900                             setResultCode(Activity.RESULT_OK);
    901                             break;
    902                         }
    903                     }
    904                 }
    905             } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
    906                 // possible force-stop
    907                 if (pkgUid != -1) {
    908                     if (DEBUG) {
    909                         Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
    910                     }
    911                     cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
    912                 }
    913             }
    914         }
    915     };
    916 
    917     private String getPackageName(Intent intent) {
    918         Uri uri = intent.getData();
    919         String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
    920         return pkg;
    921     }
    922 
    923     final private IUidObserver mUidObserver = new IUidObserver.Stub() {
    924         @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
    925             mHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget();
    926         }
    927 
    928         @Override public void onUidGone(int uid, boolean disabled) {
    929             mHandler.obtainMessage(MSG_UID_GONE, uid, disabled ? 1 : 0).sendToTarget();
    930         }
    931 
    932         @Override public void onUidActive(int uid) throws RemoteException {
    933             mHandler.obtainMessage(MSG_UID_ACTIVE, uid, 0).sendToTarget();
    934         }
    935 
    936         @Override public void onUidIdle(int uid, boolean disabled) {
    937             mHandler.obtainMessage(MSG_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget();
    938         }
    939 
    940         @Override public void onUidCachedChanged(int uid, boolean cached) {
    941         }
    942     };
    943 
    944     public Context getTestableContext() {
    945         return getContext();
    946     }
    947 
    948     public Object getLock() {
    949         return mLock;
    950     }
    951 
    952     public JobStore getJobStore() {
    953         return mJobs;
    954     }
    955 
    956     public Constants getConstants() {
    957         return mConstants;
    958     }
    959 
    960     public boolean isChainedAttributionEnabled() {
    961         return WorkSource.isChainedBatteryAttributionEnabled(getContext());
    962     }
    963 
    964     @Override
    965     public void onStartUser(int userHandle) {
    966         synchronized (mLock) {
    967             mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
    968         }
    969         // Let's kick any outstanding jobs for this user.
    970         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    971     }
    972 
    973     @Override
    974     public void onUnlockUser(int userHandle) {
    975         // Let's kick any outstanding jobs for this user.
    976         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    977     }
    978 
    979     @Override
    980     public void onStopUser(int userHandle) {
    981         synchronized (mLock) {
    982             mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
    983         }
    984     }
    985 
    986     /**
    987      * Return whether an UID is active or idle.
    988      */
    989     private boolean isUidActive(int uid) {
    990         return mAppStateTracker.isUidActiveSynced(uid);
    991     }
    992 
    993     private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
    994 
    995     public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
    996             int userId, String tag) {
    997         try {
    998             if (ActivityManager.getService().isAppStartModeDisabled(uId,
    999                     job.getService().getPackageName())) {
   1000                 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
   1001                         + " -- package not allowed to start");
   1002                 return JobScheduler.RESULT_FAILURE;
   1003             }
   1004         } catch (RemoteException e) {
   1005         }
   1006 
   1007         synchronized (mLock) {
   1008             final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
   1009 
   1010             if (work != null && toCancel != null) {
   1011                 // Fast path: we are adding work to an existing job, and the JobInfo is not
   1012                 // changing.  We can just directly enqueue this work in to the job.
   1013                 if (toCancel.getJob().equals(job)) {
   1014 
   1015                     toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
   1016 
   1017                     // If any of work item is enqueued when the source is in the foreground,
   1018                     // exempt the entire job.
   1019                     toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
   1020 
   1021                     return JobScheduler.RESULT_SUCCESS;
   1022                 }
   1023             }
   1024 
   1025             JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
   1026 
   1027             // Give exemption if the source is in the foreground just now.
   1028             // Note if it's a sync job, this method is called on the handler so it's not exactly
   1029             // the state when requestSync() was called, but that should be fine because of the
   1030             // 1 minute foreground grace period.
   1031             jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
   1032 
   1033             if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
   1034             // Jobs on behalf of others don't apply to the per-app job cap
   1035             if (ENFORCE_MAX_JOBS && packageName == null) {
   1036                 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
   1037                     Slog.w(TAG, "Too many jobs for uid " + uId);
   1038                     throw new IllegalStateException("Apps may not schedule more than "
   1039                                 + MAX_JOBS_PER_APP + " distinct jobs");
   1040                 }
   1041             }
   1042 
   1043             // This may throw a SecurityException.
   1044             jobStatus.prepareLocked(ActivityManager.getService());
   1045 
   1046             if (work != null) {
   1047                 // If work has been supplied, enqueue it into the new job.
   1048                 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
   1049             }
   1050 
   1051             if (toCancel != null) {
   1052                 // Implicitly replaces the existing job record with the new instance
   1053                 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
   1054             } else {
   1055                 startTrackingJobLocked(jobStatus, null);
   1056             }
   1057             StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
   1058                     uId, null, jobStatus.getBatteryName(),
   1059                     StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
   1060                     JobProtoEnums.STOP_REASON_CANCELLED, jobStatus.getStandbyBucket(),
   1061                     jobStatus.getJobId());
   1062 
   1063             // If the job is immediately ready to run, then we can just immediately
   1064             // put it in the pending list and try to schedule it.  This is especially
   1065             // important for jobs with a 0 deadline constraint, since they will happen a fair
   1066             // amount, we want to handle them as quickly as possible, and semantically we want to
   1067             // make sure we have started holding the wake lock for the job before returning to
   1068             // the caller.
   1069             // If the job is not yet ready to run, there is nothing more to do -- we are
   1070             // now just waiting for one of its controllers to change state and schedule
   1071             // the job appropriately.
   1072             if (isReadyToBeExecutedLocked(jobStatus)) {
   1073                 // This is a new job, we can just immediately put it on the pending
   1074                 // list and try to run it.
   1075                 mJobPackageTracker.notePending(jobStatus);
   1076                 addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
   1077                 maybeRunPendingJobsLocked();
   1078             } else {
   1079                 evaluateControllerStatesLocked(jobStatus);
   1080             }
   1081         }
   1082         return JobScheduler.RESULT_SUCCESS;
   1083     }
   1084 
   1085     public List<JobInfo> getPendingJobs(int uid) {
   1086         synchronized (mLock) {
   1087             List<JobStatus> jobs = mJobs.getJobsByUid(uid);
   1088             ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
   1089             for (int i = jobs.size() - 1; i >= 0; i--) {
   1090                 JobStatus job = jobs.get(i);
   1091                 outList.add(job.getJob());
   1092             }
   1093             return outList;
   1094         }
   1095     }
   1096 
   1097     public JobInfo getPendingJob(int uid, int jobId) {
   1098         synchronized (mLock) {
   1099             List<JobStatus> jobs = mJobs.getJobsByUid(uid);
   1100             for (int i = jobs.size() - 1; i >= 0; i--) {
   1101                 JobStatus job = jobs.get(i);
   1102                 if (job.getJobId() == jobId) {
   1103                     return job.getJob();
   1104                 }
   1105             }
   1106             return null;
   1107         }
   1108     }
   1109 
   1110     void cancelJobsForUser(int userHandle) {
   1111         synchronized (mLock) {
   1112             final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
   1113             for (int i=0; i<jobsForUser.size(); i++) {
   1114                 JobStatus toRemove = jobsForUser.get(i);
   1115                 cancelJobImplLocked(toRemove, null, "user removed");
   1116             }
   1117         }
   1118     }
   1119 
   1120     private void cancelJobsForNonExistentUsers() {
   1121         UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
   1122         synchronized (mLock) {
   1123             mJobs.removeJobsOfNonUsers(umi.getUserIds());
   1124         }
   1125     }
   1126 
   1127     void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
   1128         if ("android".equals(pkgName)) {
   1129             Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
   1130             return;
   1131         }
   1132         synchronized (mLock) {
   1133             final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
   1134             for (int i = jobsForUid.size() - 1; i >= 0; i--) {
   1135                 final JobStatus job = jobsForUid.get(i);
   1136                 if (job.getSourcePackageName().equals(pkgName)) {
   1137                     cancelJobImplLocked(job, null, reason);
   1138                 }
   1139             }
   1140         }
   1141     }
   1142 
   1143     /**
   1144      * Entry point from client to cancel all jobs originating from their uid.
   1145      * This will remove the job from the master list, and cancel the job if it was staged for
   1146      * execution or being executed.
   1147      * @param uid Uid to check against for removal of a job.
   1148      *
   1149      */
   1150     public boolean cancelJobsForUid(int uid, String reason) {
   1151         if (uid == Process.SYSTEM_UID) {
   1152             Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
   1153             return false;
   1154         }
   1155 
   1156         boolean jobsCanceled = false;
   1157         synchronized (mLock) {
   1158             final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
   1159             for (int i=0; i<jobsForUid.size(); i++) {
   1160                 JobStatus toRemove = jobsForUid.get(i);
   1161                 cancelJobImplLocked(toRemove, null, reason);
   1162                 jobsCanceled = true;
   1163             }
   1164         }
   1165         return jobsCanceled;
   1166     }
   1167 
   1168     /**
   1169      * Entry point from client to cancel the job corresponding to the jobId provided.
   1170      * This will remove the job from the master list, and cancel the job if it was staged for
   1171      * execution or being executed.
   1172      * @param uid Uid of the calling client.
   1173      * @param jobId Id of the job, provided at schedule-time.
   1174      */
   1175     public boolean cancelJob(int uid, int jobId, int callingUid) {
   1176         JobStatus toCancel;
   1177         synchronized (mLock) {
   1178             toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
   1179             if (toCancel != null) {
   1180                 cancelJobImplLocked(toCancel, null,
   1181                         "cancel() called by app, callingUid=" + callingUid
   1182                         + " uid=" + uid + " jobId=" + jobId);
   1183             }
   1184             return (toCancel != null);
   1185         }
   1186     }
   1187 
   1188     /**
   1189      * Cancel the given job, stopping it if it's currently executing.  If {@code incomingJob}
   1190      * is null, the cancelled job is removed outright from the system.  If
   1191      * {@code incomingJob} is non-null, it replaces {@code cancelled} in the store of
   1192      * currently scheduled jobs.
   1193      */
   1194     private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
   1195         if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
   1196         cancelled.unprepareLocked(ActivityManager.getService());
   1197         stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
   1198         // Remove from pending queue.
   1199         if (mPendingJobs.remove(cancelled)) {
   1200             mJobPackageTracker.noteNonpending(cancelled);
   1201         }
   1202         // Cancel if running.
   1203         stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
   1204         // If this is a replacement, bring in the new version of the job
   1205         if (incomingJob != null) {
   1206             if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString());
   1207             startTrackingJobLocked(incomingJob, cancelled);
   1208         }
   1209         reportActiveLocked();
   1210     }
   1211 
   1212     void updateUidState(int uid, int procState) {
   1213         synchronized (mLock) {
   1214             if (procState == ActivityManager.PROCESS_STATE_TOP) {
   1215                 // Only use this if we are exactly the top app.  All others can live
   1216                 // with just the foreground priority.  This means that persistent processes
   1217                 // can never be the top app priority...  that is fine.
   1218                 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
   1219             } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
   1220                 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_SERVICE);
   1221             } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
   1222                 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE);
   1223             } else {
   1224                 mUidPriorityOverride.delete(uid);
   1225             }
   1226         }
   1227     }
   1228 
   1229     @Override
   1230     public void onDeviceIdleStateChanged(boolean deviceIdle) {
   1231         synchronized (mLock) {
   1232             if (DEBUG) {
   1233                 Slog.d(TAG, "Doze state changed: " + deviceIdle);
   1234             }
   1235             if (deviceIdle) {
   1236                 // When becoming idle, make sure no jobs are actively running,
   1237                 // except those using the idle exemption flag.
   1238                 for (int i=0; i<mActiveServices.size(); i++) {
   1239                     JobServiceContext jsc = mActiveServices.get(i);
   1240                     final JobStatus executing = jsc.getRunningJobLocked();
   1241                     if (executing != null
   1242                             && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
   1243                         jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
   1244                                 "cancelled due to doze");
   1245                     }
   1246                 }
   1247             } else {
   1248                 // When coming out of idle, allow thing to start back up.
   1249                 if (mReadyToRock) {
   1250                     if (mLocalDeviceIdleController != null) {
   1251                         if (!mReportedActive) {
   1252                             mReportedActive = true;
   1253                             mLocalDeviceIdleController.setJobsActive(true);
   1254                         }
   1255                     }
   1256                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
   1257                 }
   1258             }
   1259         }
   1260     }
   1261 
   1262     void reportActiveLocked() {
   1263         // active is true if pending queue contains jobs OR some job is running.
   1264         boolean active = mPendingJobs.size() > 0;
   1265         if (mPendingJobs.size() <= 0) {
   1266             for (int i=0; i<mActiveServices.size(); i++) {
   1267                 final JobServiceContext jsc = mActiveServices.get(i);
   1268                 final JobStatus job = jsc.getRunningJobLocked();
   1269                 if (job != null
   1270                         && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
   1271                         && !job.dozeWhitelisted
   1272                         && !job.uidActive) {
   1273                     // We will report active if we have a job running and it is not an exception
   1274                     // due to being in the foreground or whitelisted.
   1275                     active = true;
   1276                     break;
   1277                 }
   1278             }
   1279         }
   1280 
   1281         if (mReportedActive != active) {
   1282             mReportedActive = active;
   1283             if (mLocalDeviceIdleController != null) {
   1284                 mLocalDeviceIdleController.setJobsActive(active);
   1285             }
   1286         }
   1287     }
   1288 
   1289     void reportAppUsage(String packageName, int userId) {
   1290         // This app just transitioned into interactive use or near equivalent, so we should
   1291         // take a look at its job state for feedback purposes.
   1292     }
   1293 
   1294     /**
   1295      * Initializes the system service.
   1296      * <p>
   1297      * Subclasses must define a single argument constructor that accepts the context
   1298      * and passes it to super.
   1299      * </p>
   1300      *
   1301      * @param context The system server context.
   1302      */
   1303     public JobSchedulerService(Context context) {
   1304         super(context);
   1305 
   1306         mLocalPM = LocalServices.getService(PackageManagerInternal.class);
   1307         mActivityManagerInternal = Preconditions.checkNotNull(
   1308                 LocalServices.getService(ActivityManagerInternal.class));
   1309 
   1310         mHandler = new JobHandler(context.getMainLooper());
   1311         mConstants = new Constants();
   1312         mConstantsObserver = new ConstantsObserver(mHandler);
   1313         mJobSchedulerStub = new JobSchedulerStub();
   1314 
   1315         mConcurrencyManager = new JobConcurrencyManager(this);
   1316 
   1317         // Set up the app standby bucketing tracker
   1318         mStandbyTracker = new StandbyTracker();
   1319         mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
   1320         mUsageStats.addAppIdleStateChangeListener(mStandbyTracker);
   1321 
   1322         // The job store needs to call back
   1323         publishLocalService(JobSchedulerInternal.class, new LocalService());
   1324 
   1325         // Initialize the job store and set up any persisted jobs
   1326         mJobs = JobStore.initAndGet(this);
   1327 
   1328         // Create the controllers.
   1329         mControllers = new ArrayList<StateController>();
   1330         mControllers.add(new ConnectivityController(this));
   1331         mControllers.add(new TimeController(this));
   1332         mControllers.add(new IdleController(this));
   1333         mBatteryController = new BatteryController(this);
   1334         mControllers.add(mBatteryController);
   1335         mStorageController = new StorageController(this);
   1336         mControllers.add(mStorageController);
   1337         mControllers.add(new BackgroundJobsController(this));
   1338         mControllers.add(new ContentObserverController(this));
   1339         mDeviceIdleJobsController = new DeviceIdleJobsController(this);
   1340         mControllers.add(mDeviceIdleJobsController);
   1341         mControllers.add(new QuotaController(this));
   1342 
   1343         // If the job store determined that it can't yet reschedule persisted jobs,
   1344         // we need to start watching the clock.
   1345         if (!mJobs.jobTimesInflatedValid()) {
   1346             Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
   1347             context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
   1348         }
   1349     }
   1350 
   1351     private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
   1352         @Override
   1353         public void onReceive(Context context, Intent intent) {
   1354             if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
   1355                 // When we reach clock sanity, recalculate the temporal windows
   1356                 // of all affected jobs.
   1357                 if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
   1358                     Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
   1359 
   1360                     // We've done our job now, so stop watching the time.
   1361                     context.unregisterReceiver(this);
   1362 
   1363                     // And kick off the work to update the affected jobs, using a secondary
   1364                     // thread instead of chugging away here on the main looper thread.
   1365                     FgThread.getHandler().post(mJobTimeUpdater);
   1366                 }
   1367             }
   1368         }
   1369     };
   1370 
   1371     private final Runnable mJobTimeUpdater = () -> {
   1372         final ArrayList<JobStatus> toRemove = new ArrayList<>();
   1373         final ArrayList<JobStatus> toAdd = new ArrayList<>();
   1374         synchronized (mLock) {
   1375             // Note: we intentionally both look up the existing affected jobs and replace them
   1376             // with recalculated ones inside the same lock lifetime.
   1377             getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove);
   1378 
   1379             // Now, at each position [i], we have both the existing JobStatus
   1380             // and the one that replaces it.
   1381             final int N = toAdd.size();
   1382             for (int i = 0; i < N; i++) {
   1383                 final JobStatus oldJob = toRemove.get(i);
   1384                 final JobStatus newJob = toAdd.get(i);
   1385                 if (DEBUG) {
   1386                     Slog.v(TAG, "  replacing " + oldJob + " with " + newJob);
   1387                 }
   1388                 cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation");
   1389             }
   1390         }
   1391     };
   1392 
   1393     @Override
   1394     public void onStart() {
   1395         publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
   1396     }
   1397 
   1398     @Override
   1399     public void onBootPhase(int phase) {
   1400         if (PHASE_SYSTEM_SERVICES_READY == phase) {
   1401             mConstantsObserver.start(getContext().getContentResolver());
   1402             for (StateController controller : mControllers) {
   1403                 controller.onSystemServicesReady();
   1404             }
   1405 
   1406             mAppStateTracker = Preconditions.checkNotNull(
   1407                     LocalServices.getService(AppStateTracker.class));
   1408             if (mConstants.USE_HEARTBEATS) {
   1409                 setNextHeartbeatAlarm();
   1410             }
   1411 
   1412             // Register br for package removals and user removals.
   1413             final IntentFilter filter = new IntentFilter();
   1414             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
   1415             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
   1416             filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
   1417             filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
   1418             filter.addDataScheme("package");
   1419             getContext().registerReceiverAsUser(
   1420                     mBroadcastReceiver, UserHandle.ALL, filter, null, null);
   1421             final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
   1422             getContext().registerReceiverAsUser(
   1423                     mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
   1424             try {
   1425                 ActivityManager.getService().registerUidObserver(mUidObserver,
   1426                         ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
   1427                         | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
   1428                         ActivityManager.PROCESS_STATE_UNKNOWN, null);
   1429             } catch (RemoteException e) {
   1430                 // ignored; both services live in system_server
   1431             }
   1432 
   1433             mConcurrencyManager.onSystemReady();
   1434 
   1435             // Remove any jobs that are not associated with any of the current users.
   1436             cancelJobsForNonExistentUsers();
   1437             // Register thermal callback
   1438             mThermalService = IThermalService.Stub.asInterface(
   1439                     ServiceManager.getService(Context.THERMAL_SERVICE));
   1440             if (mThermalService != null) {
   1441                 try {
   1442                     mThermalService.registerThermalStatusListener(new ThermalStatusListener());
   1443                 } catch (RemoteException e) {
   1444                     Slog.e(TAG, "Failed to register thermal callback.", e);
   1445                 }
   1446             }
   1447         } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
   1448             synchronized (mLock) {
   1449                 // Let's go!
   1450                 mReadyToRock = true;
   1451                 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
   1452                         BatteryStats.SERVICE_NAME));
   1453                 mLocalDeviceIdleController
   1454                         = LocalServices.getService(DeviceIdleController.LocalService.class);
   1455                 // Create the "runners".
   1456                 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
   1457                     mActiveServices.add(
   1458                             new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
   1459                                     getContext().getMainLooper()));
   1460                 }
   1461                 // Attach jobs to their controllers.
   1462                 mJobs.forEachJob((job) -> {
   1463                     for (int controller = 0; controller < mControllers.size(); controller++) {
   1464                         final StateController sc = mControllers.get(controller);
   1465                         sc.maybeStartTrackingJobLocked(job, null);
   1466                     }
   1467                 });
   1468                 // GO GO GO!
   1469                 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
   1470             }
   1471         }
   1472     }
   1473 
   1474     /**
   1475      * Called when we have a job status object that we need to insert in our
   1476      * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
   1477      * about.
   1478      */
   1479     private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
   1480         if (!jobStatus.isPreparedLocked()) {
   1481             Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
   1482         }
   1483         jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
   1484         final boolean update = mJobs.add(jobStatus);
   1485         if (mReadyToRock) {
   1486             for (int i = 0; i < mControllers.size(); i++) {
   1487                 StateController controller = mControllers.get(i);
   1488                 if (update) {
   1489                     controller.maybeStopTrackingJobLocked(jobStatus, null, true);
   1490                 }
   1491                 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
   1492             }
   1493         }
   1494     }
   1495 
   1496     /**
   1497      * Called when we want to remove a JobStatus object that we've finished executing. Returns the
   1498      * object removed.
   1499      */
   1500     private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
   1501             boolean writeBack) {
   1502         // Deal with any remaining work items in the old job.
   1503         jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
   1504 
   1505         // Remove from store as well as controllers.
   1506         final boolean removed = mJobs.remove(jobStatus, writeBack);
   1507         if (removed && mReadyToRock) {
   1508             for (int i=0; i<mControllers.size(); i++) {
   1509                 StateController controller = mControllers.get(i);
   1510                 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
   1511             }
   1512         }
   1513         return removed;
   1514     }
   1515 
   1516     private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
   1517         for (int i=0; i<mActiveServices.size(); i++) {
   1518             JobServiceContext jsc = mActiveServices.get(i);
   1519             final JobStatus executing = jsc.getRunningJobLocked();
   1520             if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
   1521                 jsc.cancelExecutingJobLocked(reason, debugReason);
   1522                 return true;
   1523             }
   1524         }
   1525         return false;
   1526     }
   1527 
   1528     /**
   1529      * @param job JobStatus we are querying against.
   1530      * @return Whether or not the job represented by the status object is currently being run or
   1531      * is pending.
   1532      */
   1533     private boolean isCurrentlyActiveLocked(JobStatus job) {
   1534         for (int i=0; i<mActiveServices.size(); i++) {
   1535             JobServiceContext serviceContext = mActiveServices.get(i);
   1536             final JobStatus running = serviceContext.getRunningJobLocked();
   1537             if (running != null && running.matches(job.getUid(), job.getJobId())) {
   1538                 return true;
   1539             }
   1540         }
   1541         return false;
   1542     }
   1543 
   1544     void noteJobsPending(List<JobStatus> jobs) {
   1545         for (int i = jobs.size() - 1; i >= 0; i--) {
   1546             JobStatus job = jobs.get(i);
   1547             mJobPackageTracker.notePending(job);
   1548         }
   1549     }
   1550 
   1551     void noteJobsNonpending(List<JobStatus> jobs) {
   1552         for (int i = jobs.size() - 1; i >= 0; i--) {
   1553             JobStatus job = jobs.get(i);
   1554             mJobPackageTracker.noteNonpending(job);
   1555         }
   1556     }
   1557 
   1558     /**
   1559      * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
   1560      * specify an override deadline on a failed job (the failed job will run even though it's not
   1561      * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
   1562      * ready job with {@link JobStatus#getNumFailures()} > 0 will be executed.
   1563      *
   1564      * @param failureToReschedule Provided job status that we will reschedule.
   1565      * @return A newly instantiated JobStatus with the same constraints as the last job except
   1566      * with adjusted timing constraints.
   1567      *
   1568      * @see #maybeQueueReadyJobsForExecutionLocked
   1569      */
   1570     @VisibleForTesting
   1571     JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
   1572         final long elapsedNowMillis = sElapsedRealtimeClock.millis();
   1573         final JobInfo job = failureToReschedule.getJob();
   1574 
   1575         final long initialBackoffMillis = job.getInitialBackoffMillis();
   1576         final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
   1577         long delayMillis;
   1578 
   1579         if (failureToReschedule.hasWorkLocked()) {
   1580             if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
   1581                 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
   1582                         + backoffAttempts + " > work limit "
   1583                         + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
   1584                 return null;
   1585             }
   1586         } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
   1587             Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
   1588                     + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
   1589             return null;
   1590         }
   1591 
   1592         switch (job.getBackoffPolicy()) {
   1593             case JobInfo.BACKOFF_POLICY_LINEAR: {
   1594                 long backoff = initialBackoffMillis;
   1595                 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
   1596                     backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
   1597                 }
   1598                 delayMillis = backoff * backoffAttempts;
   1599             } break;
   1600             default:
   1601                 if (DEBUG) {
   1602                     Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
   1603                 }
   1604             case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
   1605                 long backoff = initialBackoffMillis;
   1606                 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
   1607                     backoff = mConstants.MIN_EXP_BACKOFF_TIME;
   1608                 }
   1609                 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
   1610             } break;
   1611         }
   1612         delayMillis =
   1613                 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
   1614         JobStatus newJob = new JobStatus(failureToReschedule, getCurrentHeartbeat(),
   1615                 elapsedNowMillis + delayMillis,
   1616                 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
   1617                 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
   1618         if (job.isPeriodic()) {
   1619             newJob.setOriginalLatestRunTimeElapsed(
   1620                     failureToReschedule.getOriginalLatestRunTimeElapsed());
   1621         }
   1622         for (int ic=0; ic<mControllers.size(); ic++) {
   1623             StateController controller = mControllers.get(ic);
   1624             controller.rescheduleForFailureLocked(newJob, failureToReschedule);
   1625         }
   1626         return newJob;
   1627     }
   1628 
   1629     /**
   1630      * Maximum time buffer in which JobScheduler will try to optimize periodic job scheduling. This
   1631      * does not cause a job's period to be larger than requested (eg: if the requested period is
   1632      * shorter than this buffer). This is used to put a limit on when JobScheduler will intervene
   1633      * and try to optimize scheduling if the current job finished less than this amount of time to
   1634      * the start of the next period
   1635      */
   1636     private static final long PERIODIC_JOB_WINDOW_BUFFER = 30 * MINUTE_IN_MILLIS;
   1637 
   1638     /** The maximum period a periodic job can have. Anything higher will be clamped down to this. */
   1639     public static final long MAX_ALLOWED_PERIOD_MS = 365 * 24 * 60 * 60 * 1000L;
   1640 
   1641     /**
   1642      * Called after a periodic has executed so we can reschedule it. We take the last execution
   1643      * time of the job to be the time of completion (i.e. the time at which this function is
   1644      * called).
   1645      * <p>This could be inaccurate b/c the job can run for as long as
   1646      * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
   1647      * to underscheduling at least, rather than if we had taken the last execution time to be the
   1648      * start of the execution.
   1649      * <p>Unlike a reschedule prior to execution, in this case we advance the next-heartbeat
   1650      * tracking as though the job were newly-scheduled.
   1651      * @return A new job representing the execution criteria for this instantiation of the
   1652      * recurring job.
   1653      */
   1654     @VisibleForTesting
   1655     JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
   1656         final long elapsedNow = sElapsedRealtimeClock.millis();
   1657         final long newLatestRuntimeElapsed;
   1658         // Make sure period is in the interval [min_possible_period, max_possible_period].
   1659         final long period = Math.max(JobInfo.getMinPeriodMillis(),
   1660                 Math.min(MAX_ALLOWED_PERIOD_MS, periodicToReschedule.getJob().getIntervalMillis()));
   1661         // Make sure flex is in the interval [min_possible_flex, period].
   1662         final long flex = Math.max(JobInfo.getMinFlexMillis(),
   1663                 Math.min(period, periodicToReschedule.getJob().getFlexMillis()));
   1664         long rescheduleBuffer = 0;
   1665 
   1666         long olrte = periodicToReschedule.getOriginalLatestRunTimeElapsed();
   1667         if (olrte < 0 || olrte == JobStatus.NO_LATEST_RUNTIME) {
   1668             Slog.wtf(TAG, "Invalid periodic job original latest run time: " + olrte);
   1669             olrte = elapsedNow;
   1670         }
   1671         final long latestRunTimeElapsed = olrte;
   1672 
   1673         final long diffMs = Math.abs(elapsedNow - latestRunTimeElapsed);
   1674         if (elapsedNow > latestRunTimeElapsed) {
   1675             // The job ran past its expected run window. Have it count towards the current window
   1676             // and schedule a new job for the next window.
   1677             if (DEBUG) {
   1678                 Slog.i(TAG, "Periodic job ran after its intended window.");
   1679             }
   1680             long numSkippedWindows = (diffMs / period) + 1; // +1 to include original window
   1681             if (period != flex && diffMs > Math.min(PERIODIC_JOB_WINDOW_BUFFER,
   1682                     (period - flex) / 2)) {
   1683                 if (DEBUG) {
   1684                     Slog.d(TAG, "Custom flex job ran too close to next window.");
   1685                 }
   1686                 // For custom flex periods, if the job was run too close to the next window,
   1687                 // skip the next window and schedule for the following one.
   1688                 numSkippedWindows += 1;
   1689             }
   1690             newLatestRuntimeElapsed = latestRunTimeElapsed + (period * numSkippedWindows);
   1691         } else {
   1692             newLatestRuntimeElapsed = latestRunTimeElapsed + period;
   1693             if (diffMs < PERIODIC_JOB_WINDOW_BUFFER && diffMs < period / 6) {
   1694                 // Add a little buffer to the start of the next window so the job doesn't run
   1695                 // too soon after this completed one.
   1696                 rescheduleBuffer = Math.min(PERIODIC_JOB_WINDOW_BUFFER, period / 6 - diffMs);
   1697             }
   1698         }
   1699 
   1700         if (newLatestRuntimeElapsed < elapsedNow) {
   1701             Slog.wtf(TAG, "Rescheduling calculated latest runtime in the past: "
   1702                     + newLatestRuntimeElapsed);
   1703             return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
   1704                     elapsedNow + period - flex, elapsedNow + period,
   1705                     0 /* backoffAttempt */,
   1706                     sSystemClock.millis() /* lastSuccessfulRunTime */,
   1707                     periodicToReschedule.getLastFailedRunTime());
   1708         }
   1709 
   1710         final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed
   1711                 - Math.min(flex, period - rescheduleBuffer);
   1712 
   1713         if (DEBUG) {
   1714             Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
   1715                     newEarliestRunTimeElapsed / 1000 + ", " + newLatestRuntimeElapsed / 1000
   1716                     + "]s");
   1717         }
   1718         return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
   1719                 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
   1720                 0 /* backoffAttempt */,
   1721                 sSystemClock.millis() /* lastSuccessfulRunTime */,
   1722                 periodicToReschedule.getLastFailedRunTime());
   1723     }
   1724 
   1725     /*
   1726      * We default to "long enough ago that every bucket's jobs are immediately runnable" to
   1727      * avoid starvation of apps in uncommon-use buckets that might arise from repeated
   1728      * reboot behavior.
   1729      */
   1730     long heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId) {
   1731         // The furthest back in pre-boot time that we need to bother with
   1732         long heartbeat = -mConstants.STANDBY_BEATS[RARE_INDEX];
   1733         boolean cacheHit = false;
   1734         synchronized (mLock) {
   1735             HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
   1736             if (jobPackages != null) {
   1737                 long cachedValue = jobPackages.getOrDefault(packageName, Long.MAX_VALUE);
   1738                 if (cachedValue < Long.MAX_VALUE) {
   1739                     cacheHit = true;
   1740                     heartbeat = cachedValue;
   1741                 }
   1742             }
   1743             if (!cacheHit) {
   1744                 // We haven't seen it yet; ask usage stats about it
   1745                 final long timeSinceJob = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
   1746                 if (timeSinceJob < Long.MAX_VALUE) {
   1747                     // Usage stats knows about it from before, so calculate back from that
   1748                     // and go from there.
   1749                     heartbeat = mHeartbeat - (timeSinceJob / mConstants.STANDBY_HEARTBEAT_TIME);
   1750                 }
   1751                 // If usage stats returned its "not found" MAX_VALUE, we still have the
   1752                 // negative default 'heartbeat' value we established above
   1753                 setLastJobHeartbeatLocked(packageName, userId, heartbeat);
   1754             }
   1755         }
   1756         if (DEBUG_STANDBY) {
   1757             Slog.v(TAG, "Last job heartbeat " + heartbeat + " for "
   1758                     + packageName + "/" + userId);
   1759         }
   1760         return heartbeat;
   1761     }
   1762 
   1763     long heartbeatWhenJobsLastRun(JobStatus job) {
   1764         return heartbeatWhenJobsLastRun(job.getSourcePackageName(), job.getSourceUserId());
   1765     }
   1766 
   1767     void setLastJobHeartbeatLocked(String packageName, int userId, long heartbeat) {
   1768         HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
   1769         if (jobPackages == null) {
   1770             jobPackages = new HashMap<>();
   1771             mLastJobHeartbeats.put(userId, jobPackages);
   1772         }
   1773         jobPackages.put(packageName, heartbeat);
   1774     }
   1775 
   1776     // JobCompletedListener implementations.
   1777 
   1778     /**
   1779      * A job just finished executing. We fetch the
   1780      * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
   1781      * whether we want to reschedule we re-add it to the controllers.
   1782      * @param jobStatus Completed job.
   1783      * @param needsReschedule Whether the implementing class should reschedule this job.
   1784      */
   1785     @Override
   1786     public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
   1787         if (DEBUG) {
   1788             Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
   1789         }
   1790 
   1791         // If the job wants to be rescheduled, we first need to make the next upcoming
   1792         // job so we can transfer any appropriate state over from the previous job when
   1793         // we stop it.
   1794         final JobStatus rescheduledJob = needsReschedule
   1795                 ? getRescheduleJobForFailureLocked(jobStatus) : null;
   1796 
   1797         // Do not write back immediately if this is a periodic job. The job may get lost if system
   1798         // shuts down before it is added back.
   1799         if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
   1800             if (DEBUG) {
   1801                 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
   1802             }
   1803             // We still want to check for jobs to execute, because this job may have
   1804             // scheduled a new job under the same job id, and now we can run it.
   1805             mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
   1806             return;
   1807         }
   1808 
   1809         if (rescheduledJob != null) {
   1810             try {
   1811                 rescheduledJob.prepareLocked(ActivityManager.getService());
   1812             } catch (SecurityException e) {
   1813                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
   1814             }
   1815             startTrackingJobLocked(rescheduledJob, jobStatus);
   1816         } else if (jobStatus.getJob().isPeriodic()) {
   1817             JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
   1818             try {
   1819                 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
   1820             } catch (SecurityException e) {
   1821                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
   1822             }
   1823             startTrackingJobLocked(rescheduledPeriodic, jobStatus);
   1824         }
   1825         jobStatus.unprepareLocked(ActivityManager.getService());
   1826         reportActiveLocked();
   1827         mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
   1828     }
   1829 
   1830     // StateChangedListener implementations.
   1831 
   1832     /**
   1833      * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
   1834      * some controller's state has changed, so as to run through the list of jobs and start/stop
   1835      * any that are eligible.
   1836      */
   1837     @Override
   1838     public void onControllerStateChanged() {
   1839         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
   1840     }
   1841 
   1842     @Override
   1843     public void onRunJobNow(JobStatus jobStatus) {
   1844         mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
   1845     }
   1846 
   1847     final private class JobHandler extends Handler {
   1848 
   1849         public JobHandler(Looper looper) {
   1850             super(looper);
   1851         }
   1852 
   1853         @Override
   1854         public void handleMessage(Message message) {
   1855             synchronized (mLock) {
   1856                 if (!mReadyToRock) {
   1857                     return;
   1858                 }
   1859                 switch (message.what) {
   1860                     case MSG_JOB_EXPIRED: {
   1861                         JobStatus runNow = (JobStatus) message.obj;
   1862                         // runNow can be null, which is a controller's way of indicating that its
   1863                         // state is such that all ready jobs should be run immediately.
   1864                         if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
   1865                             mJobPackageTracker.notePending(runNow);
   1866                             addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
   1867                         } else {
   1868                             queueReadyJobsForExecutionLocked();
   1869                         }
   1870                     } break;
   1871                     case MSG_CHECK_JOB:
   1872                         if (DEBUG) {
   1873                             Slog.d(TAG, "MSG_CHECK_JOB");
   1874                         }
   1875                         removeMessages(MSG_CHECK_JOB);
   1876                         if (mReportedActive) {
   1877                             // if jobs are currently being run, queue all ready jobs for execution.
   1878                             queueReadyJobsForExecutionLocked();
   1879                         } else {
   1880                             // Check the list of jobs and run some of them if we feel inclined.
   1881                             maybeQueueReadyJobsForExecutionLocked();
   1882                         }
   1883                         break;
   1884                     case MSG_CHECK_JOB_GREEDY:
   1885                         if (DEBUG) {
   1886                             Slog.d(TAG, "MSG_CHECK_JOB_GREEDY");
   1887                         }
   1888                         queueReadyJobsForExecutionLocked();
   1889                         break;
   1890                     case MSG_STOP_JOB:
   1891                         cancelJobImplLocked((JobStatus) message.obj, null,
   1892                                 "app no longer allowed to run");
   1893                         break;
   1894 
   1895                     case MSG_UID_STATE_CHANGED: {
   1896                         final int uid = message.arg1;
   1897                         final int procState = message.arg2;
   1898                         updateUidState(uid, procState);
   1899                         break;
   1900                     }
   1901                     case MSG_UID_GONE: {
   1902                         final int uid = message.arg1;
   1903                         final boolean disabled = message.arg2 != 0;
   1904                         updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
   1905                         if (disabled) {
   1906                             cancelJobsForUid(uid, "uid gone");
   1907                         }
   1908                         synchronized (mLock) {
   1909                             mDeviceIdleJobsController.setUidActiveLocked(uid, false);
   1910                         }
   1911                         break;
   1912                     }
   1913                     case MSG_UID_ACTIVE: {
   1914                         final int uid = message.arg1;
   1915                         synchronized (mLock) {
   1916                             mDeviceIdleJobsController.setUidActiveLocked(uid, true);
   1917                         }
   1918                         break;
   1919                     }
   1920                     case MSG_UID_IDLE: {
   1921                         final int uid = message.arg1;
   1922                         final boolean disabled = message.arg2 != 0;
   1923                         if (disabled) {
   1924                             cancelJobsForUid(uid, "app uid idle");
   1925                         }
   1926                         synchronized (mLock) {
   1927                             mDeviceIdleJobsController.setUidActiveLocked(uid, false);
   1928                         }
   1929                         break;
   1930                     }
   1931 
   1932                 }
   1933                 maybeRunPendingJobsLocked();
   1934                 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
   1935             }
   1936         }
   1937     }
   1938 
   1939     private boolean isJobThermalConstrainedLocked(JobStatus job) {
   1940         return mThermalConstraint && job.hasConnectivityConstraint()
   1941                 && (evaluateJobPriorityLocked(job) < JobInfo.PRIORITY_FOREGROUND_APP);
   1942     }
   1943 
   1944     private void stopNonReadyActiveJobsLocked() {
   1945         for (int i=0; i<mActiveServices.size(); i++) {
   1946             JobServiceContext serviceContext = mActiveServices.get(i);
   1947             final JobStatus running = serviceContext.getRunningJobLocked();
   1948             if (running == null) {
   1949                 continue;
   1950             }
   1951             if (!running.isReady()) {
   1952                 serviceContext.cancelExecutingJobLocked(
   1953                         JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
   1954                         "cancelled due to unsatisfied constraints");
   1955             } else if (isJobThermalConstrainedLocked(running)) {
   1956                 serviceContext.cancelExecutingJobLocked(
   1957                         JobParameters.REASON_DEVICE_THERMAL,
   1958                         "cancelled due to thermal condition");
   1959             }
   1960         }
   1961     }
   1962 
   1963     /**
   1964      * Run through list of jobs and execute all possible - at least one is expired so we do
   1965      * as many as we can.
   1966      */
   1967     private void queueReadyJobsForExecutionLocked() {
   1968         if (DEBUG) {
   1969             Slog.d(TAG, "queuing all ready jobs for execution:");
   1970         }
   1971         noteJobsNonpending(mPendingJobs);
   1972         mPendingJobs.clear();
   1973         stopNonReadyActiveJobsLocked();
   1974         mJobs.forEachJob(mReadyQueueFunctor);
   1975         mReadyQueueFunctor.postProcess();
   1976 
   1977         if (DEBUG) {
   1978             final int queuedJobs = mPendingJobs.size();
   1979             if (queuedJobs == 0) {
   1980                 Slog.d(TAG, "No jobs pending.");
   1981             } else {
   1982                 Slog.d(TAG, queuedJobs + " jobs queued.");
   1983             }
   1984         }
   1985     }
   1986 
   1987     final class ReadyJobQueueFunctor implements Consumer<JobStatus> {
   1988         ArrayList<JobStatus> newReadyJobs;
   1989 
   1990         @Override
   1991         public void accept(JobStatus job) {
   1992             if (isReadyToBeExecutedLocked(job)) {
   1993                 if (DEBUG) {
   1994                     Slog.d(TAG, "    queued " + job.toShortString());
   1995                 }
   1996                 if (newReadyJobs == null) {
   1997                     newReadyJobs = new ArrayList<JobStatus>();
   1998                 }
   1999                 newReadyJobs.add(job);
   2000             } else {
   2001                 evaluateControllerStatesLocked(job);
   2002             }
   2003         }
   2004 
   2005         public void postProcess() {
   2006             if (newReadyJobs != null) {
   2007                 noteJobsPending(newReadyJobs);
   2008                 mPendingJobs.addAll(newReadyJobs);
   2009                 if (mPendingJobs.size() > 1) {
   2010                     mPendingJobs.sort(mEnqueueTimeComparator);
   2011                 }
   2012             }
   2013             newReadyJobs = null;
   2014         }
   2015     }
   2016     private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
   2017 
   2018     /**
   2019      * The state of at least one job has changed. Here is where we could enforce various
   2020      * policies on when we want to execute jobs.
   2021      */
   2022     final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> {
   2023         int chargingCount;
   2024         int batteryNotLowCount;
   2025         int storageNotLowCount;
   2026         int idleCount;
   2027         int backoffCount;
   2028         int connectivityCount;
   2029         int contentCount;
   2030         List<JobStatus> runnableJobs;
   2031 
   2032         public MaybeReadyJobQueueFunctor() {
   2033             reset();
   2034         }
   2035 
   2036         // Functor method invoked for each job via JobStore.forEachJob()
   2037         @Override
   2038         public void accept(JobStatus job) {
   2039             if (isReadyToBeExecutedLocked(job)) {
   2040                 try {
   2041                     if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
   2042                             job.getJob().getService().getPackageName())) {
   2043                         Slog.w(TAG, "Aborting job " + job.getUid() + ":"
   2044                                 + job.getJob().toString() + " -- package not allowed to start");
   2045                         mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
   2046                         return;
   2047                     }
   2048                 } catch (RemoteException e) {
   2049                 }
   2050                 if (job.getNumFailures() > 0) {
   2051                     backoffCount++;
   2052                 }
   2053                 if (job.hasIdleConstraint()) {
   2054                     idleCount++;
   2055                 }
   2056                 if (job.hasConnectivityConstraint()) {
   2057                     connectivityCount++;
   2058                 }
   2059                 if (job.hasChargingConstraint()) {
   2060                     chargingCount++;
   2061                 }
   2062                 if (job.hasBatteryNotLowConstraint()) {
   2063                     batteryNotLowCount++;
   2064                 }
   2065                 if (job.hasStorageNotLowConstraint()) {
   2066                     storageNotLowCount++;
   2067                 }
   2068                 if (job.hasContentTriggerConstraint()) {
   2069                     contentCount++;
   2070                 }
   2071                 if (runnableJobs == null) {
   2072                     runnableJobs = new ArrayList<>();
   2073                 }
   2074                 runnableJobs.add(job);
   2075             } else {
   2076                 evaluateControllerStatesLocked(job);
   2077             }
   2078         }
   2079 
   2080         public void postProcess() {
   2081             if (backoffCount > 0 ||
   2082                     idleCount >= mConstants.MIN_IDLE_COUNT ||
   2083                     connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
   2084                     chargingCount >= mConstants.MIN_CHARGING_COUNT ||
   2085                     batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
   2086                     storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
   2087                     contentCount >= mConstants.MIN_CONTENT_COUNT ||
   2088                     (runnableJobs != null
   2089                             && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
   2090                 if (DEBUG) {
   2091                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
   2092                 }
   2093                 noteJobsPending(runnableJobs);
   2094                 mPendingJobs.addAll(runnableJobs);
   2095                 if (mPendingJobs.size() > 1) {
   2096                     mPendingJobs.sort(mEnqueueTimeComparator);
   2097                 }
   2098             } else {
   2099                 if (DEBUG) {
   2100                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
   2101                 }
   2102             }
   2103 
   2104             // Be ready for next time
   2105             reset();
   2106         }
   2107 
   2108         private void reset() {
   2109             chargingCount = 0;
   2110             idleCount =  0;
   2111             backoffCount = 0;
   2112             connectivityCount = 0;
   2113             batteryNotLowCount = 0;
   2114             storageNotLowCount = 0;
   2115             contentCount = 0;
   2116             runnableJobs = null;
   2117         }
   2118     }
   2119     private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
   2120 
   2121     private void maybeQueueReadyJobsForExecutionLocked() {
   2122         if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
   2123 
   2124         noteJobsNonpending(mPendingJobs);
   2125         mPendingJobs.clear();
   2126         stopNonReadyActiveJobsLocked();
   2127         mJobs.forEachJob(mMaybeQueueFunctor);
   2128         mMaybeQueueFunctor.postProcess();
   2129     }
   2130 
   2131     /**
   2132      * Heartbeat tracking.  The heartbeat alarm is intentionally non-wakeup.
   2133      */
   2134     class HeartbeatAlarmListener implements AlarmManager.OnAlarmListener {
   2135 
   2136         @Override
   2137         public void onAlarm() {
   2138             synchronized (mLock) {
   2139                 final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
   2140                 final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
   2141                 if (beatsElapsed > 0) {
   2142                     mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
   2143                     advanceHeartbeatLocked(beatsElapsed);
   2144                 }
   2145             }
   2146             setNextHeartbeatAlarm();
   2147         }
   2148     }
   2149 
   2150     // Intentionally does not touch the alarm timing
   2151     void advanceHeartbeatLocked(long beatsElapsed) {
   2152         if (!mConstants.USE_HEARTBEATS) {
   2153             return;
   2154         }
   2155         mHeartbeat += beatsElapsed;
   2156         if (DEBUG_STANDBY) {
   2157             Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
   2158                     + " to " + mHeartbeat);
   2159         }
   2160         // Don't update ACTIVE or NEVER bucket milestones.  Note that mHeartbeat
   2161         // will be equal to mNextBucketHeartbeat[bucket] for one beat, during which
   2162         // new jobs scheduled by apps in that bucket will be permitted to run
   2163         // immediately.
   2164         boolean didAdvanceBucket = false;
   2165         for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) {
   2166             // Did we reach or cross a bucket boundary?
   2167             if (mHeartbeat >= mNextBucketHeartbeat[i]) {
   2168                 didAdvanceBucket = true;
   2169             }
   2170             while (mHeartbeat > mNextBucketHeartbeat[i]) {
   2171                 mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i];
   2172             }
   2173             if (DEBUG_STANDBY) {
   2174                 Slog.v(TAG, "   Bucket " + i + " next heartbeat "
   2175                         + mNextBucketHeartbeat[i]);
   2176             }
   2177         }
   2178 
   2179         if (didAdvanceBucket) {
   2180             if (DEBUG_STANDBY) {
   2181                 Slog.v(TAG, "Hit bucket boundary; reevaluating job runnability");
   2182             }
   2183             mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
   2184         }
   2185     }
   2186 
   2187     void setNextHeartbeatAlarm() {
   2188         final long heartbeatLength;
   2189         synchronized (mLock) {
   2190             if (!mConstants.USE_HEARTBEATS) {
   2191                 return;
   2192             }
   2193             heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
   2194         }
   2195         final long now = sElapsedRealtimeClock.millis();
   2196         final long nextBeatOrdinal = (now + heartbeatLength) / heartbeatLength;
   2197         final long nextHeartbeat = nextBeatOrdinal * heartbeatLength;
   2198         if (DEBUG_STANDBY) {
   2199             Slog.i(TAG, "Setting heartbeat alarm for " + nextHeartbeat
   2200                     + " = " + TimeUtils.formatDuration(nextHeartbeat - now));
   2201         }
   2202         AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
   2203         am.setExact(AlarmManager.ELAPSED_REALTIME, nextHeartbeat,
   2204                 HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
   2205     }
   2206 
   2207     /** Returns true if both the calling and source users for the job are started. */
   2208     private boolean areUsersStartedLocked(final JobStatus job) {
   2209         boolean sourceStarted = ArrayUtils.contains(mStartedUsers, job.getSourceUserId());
   2210         if (job.getUserId() == job.getSourceUserId()) {
   2211             return sourceStarted;
   2212         }
   2213         return sourceStarted && ArrayUtils.contains(mStartedUsers, job.getUserId());
   2214     }
   2215 
   2216     /**
   2217      * Criteria for moving a job into the pending queue:
   2218      *      - It's ready.
   2219      *      - It's not pending.
   2220      *      - It's not already running on a JSC.
   2221      *      - The user that requested the job is running.
   2222      *      - The job's standby bucket has come due to be runnable.
   2223      *      - The component is enabled and runnable.
   2224      */
   2225     private boolean isReadyToBeExecutedLocked(JobStatus job) {
   2226         final boolean jobReady = job.isReady();
   2227 
   2228         if (DEBUG) {
   2229             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
   2230                     + " ready=" + jobReady);
   2231         }
   2232 
   2233         // This is a condition that is very likely to be false (most jobs that are
   2234         // scheduled are sitting there, not ready yet) and very cheap to check (just
   2235         // a few conditions on data in JobStatus).
   2236         if (!jobReady) {
   2237             if (job.getSourcePackageName().equals("android.jobscheduler.cts.jobtestapp")) {
   2238                 Slog.v(TAG, "    NOT READY: " + job);
   2239             }
   2240             return false;
   2241         }
   2242 
   2243         final boolean jobExists = mJobs.containsJob(job);
   2244 
   2245         final boolean userStarted = areUsersStartedLocked(job);
   2246 
   2247         if (DEBUG) {
   2248             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
   2249                     + " exists=" + jobExists + " userStarted=" + userStarted);
   2250         }
   2251 
   2252         // These are also fairly cheap to check, though they typically will not
   2253         // be conditions we fail.
   2254         if (!jobExists || !userStarted) {
   2255             return false;
   2256         }
   2257 
   2258         if (isJobThermalConstrainedLocked(job)) {
   2259             return false;
   2260         }
   2261 
   2262         final boolean jobPending = mPendingJobs.contains(job);
   2263         final boolean jobActive = isCurrentlyActiveLocked(job);
   2264 
   2265         if (DEBUG) {
   2266             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
   2267                     + " pending=" + jobPending + " active=" + jobActive);
   2268         }
   2269 
   2270         // These can be a little more expensive (especially jobActive, since we need to
   2271         // go through the array of all potentially active jobs), so we are doing them
   2272         // later...  but still before checking with the package manager!
   2273         if (jobPending || jobActive) {
   2274             return false;
   2275         }
   2276 
   2277         if (mConstants.USE_HEARTBEATS) {
   2278             // If the app is in a non-active standby bucket, make sure we've waited
   2279             // an appropriate amount of time since the last invocation.  During device-
   2280             // wide parole, standby bucketing is ignored.
   2281             //
   2282             // Jobs in 'active' apps are not subject to standby, nor are jobs that are
   2283             // specifically marked as exempt.
   2284             if (DEBUG_STANDBY) {
   2285                 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
   2286                         + " parole=" + mInParole + " active=" + job.uidActive
   2287                         + " exempt=" + job.getJob().isExemptedFromAppStandby());
   2288             }
   2289             if (!mInParole
   2290                     && !job.uidActive
   2291                     && !job.getJob().isExemptedFromAppStandby()) {
   2292                 final int bucket = job.getStandbyBucket();
   2293                 if (DEBUG_STANDBY) {
   2294                     Slog.v(TAG, "  bucket=" + bucket + " heartbeat=" + mHeartbeat
   2295                             + " next=" + mNextBucketHeartbeat[bucket]);
   2296                 }
   2297                 if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
   2298                     // Only skip this job if the app is still waiting for the end of its nominal
   2299                     // bucket interval.  Once it's waited that long, we let it go ahead and clear.
   2300                     // The final (NEVER) bucket is special; we never age those apps' jobs into
   2301                     // runnability.
   2302                     final long appLastRan = heartbeatWhenJobsLastRun(job);
   2303                     if (bucket >= mConstants.STANDBY_BEATS.length
   2304                             || (mHeartbeat > appLastRan
   2305                             && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
   2306                         if (job.getWhenStandbyDeferred() == 0) {
   2307                             if (DEBUG_STANDBY) {
   2308                                 Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
   2309                                         + (appLastRan + mConstants.STANDBY_BEATS[bucket])
   2310                                         + " for " + job);
   2311                             }
   2312                             job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
   2313                         }
   2314                         return false;
   2315                     } else {
   2316                         if (DEBUG_STANDBY) {
   2317                             Slog.v(TAG, "Bucket deferred job aged into runnability at "
   2318                                     + mHeartbeat + " : " + job);
   2319                         }
   2320                     }
   2321                 }
   2322             }
   2323         }
   2324 
   2325         // The expensive check: validate that the defined package+service is
   2326         // still present & viable.
   2327         return isComponentUsable(job);
   2328     }
   2329 
   2330     private boolean isComponentUsable(@NonNull JobStatus job) {
   2331         final ServiceInfo service;
   2332         try {
   2333             // TODO: cache result until we're notified that something in the package changed.
   2334             service = AppGlobals.getPackageManager().getServiceInfo(
   2335                     job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
   2336                     job.getUserId());
   2337         } catch (RemoteException e) {
   2338             throw e.rethrowAsRuntimeException();
   2339         }
   2340 
   2341         if (service == null) {
   2342             if (DEBUG) {
   2343                 Slog.v(TAG, "isComponentUsable: " + job.toShortString()
   2344                         + " component not present");
   2345             }
   2346             return false;
   2347         }
   2348 
   2349         // Everything else checked out so far, so this is the final yes/no check
   2350         final boolean appIsBad = mActivityManagerInternal.isAppBad(service.applicationInfo);
   2351         if (DEBUG && appIsBad) {
   2352             Slog.i(TAG, "App is bad for " + job.toShortString() + " so not runnable");
   2353         }
   2354         return !appIsBad;
   2355     }
   2356 
   2357     private void evaluateControllerStatesLocked(final JobStatus job) {
   2358         for (int c = mControllers.size() - 1; c >= 0; --c) {
   2359             final StateController sc = mControllers.get(c);
   2360             sc.evaluateStateLocked(job);
   2361         }
   2362     }
   2363 
   2364     /**
   2365      * Returns true if non-job constraint components are in place -- if job.isReady() returns true
   2366      * and this method returns true, then the job is ready to be executed.
   2367      */
   2368     public boolean areComponentsInPlaceLocked(JobStatus job) {
   2369         // This code is very similar to the code in isReadyToBeExecutedLocked --- it uses the same
   2370         // conditions.
   2371 
   2372         final boolean jobExists = mJobs.containsJob(job);
   2373         final boolean userStarted = areUsersStartedLocked(job);
   2374 
   2375         if (DEBUG) {
   2376             Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
   2377                     + " exists=" + jobExists + " userStarted=" + userStarted);
   2378         }
   2379 
   2380         // These are also fairly cheap to check, though they typically will not
   2381         // be conditions we fail.
   2382         if (!jobExists || !userStarted) {
   2383             return false;
   2384         }
   2385 
   2386         if (isJobThermalConstrainedLocked(job)) {
   2387             return false;
   2388         }
   2389 
   2390         // Job pending/active doesn't affect the readiness of a job.
   2391 
   2392         // Skipping the heartbeat check as this will only come into play when using the rolling
   2393         // window quota management system.
   2394 
   2395         // The expensive check: validate that the defined package+service is
   2396         // still present & viable.
   2397         return isComponentUsable(job);
   2398     }
   2399 
   2400     /**
   2401      * Reconcile jobs in the pending queue against available execution contexts.
   2402      * A controller can force a job into the pending queue even if it's already running, but
   2403      * here is where we decide whether to actually execute it.
   2404      */
   2405     void maybeRunPendingJobsLocked() {
   2406         if (DEBUG) {
   2407             Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
   2408         }
   2409         mConcurrencyManager.assignJobsToContextsLocked();
   2410         reportActiveLocked();
   2411     }
   2412 
   2413     private int adjustJobPriority(int curPriority, JobStatus job) {
   2414         if (curPriority < JobInfo.PRIORITY_TOP_APP) {
   2415             float factor = mJobPackageTracker.getLoadFactor(job);
   2416             if (factor >= mConstants.HEAVY_USE_FACTOR) {
   2417                 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
   2418             } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
   2419                 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
   2420             }
   2421         }
   2422         return curPriority;
   2423     }
   2424 
   2425     int evaluateJobPriorityLocked(JobStatus job) {
   2426         int priority = job.getPriority();
   2427         if (priority >= JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE) {
   2428             return adjustJobPriority(priority, job);
   2429         }
   2430         int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
   2431         if (override != 0) {
   2432             return adjustJobPriority(override, job);
   2433         }
   2434         return adjustJobPriority(priority, job);
   2435     }
   2436 
   2437     final class LocalService implements JobSchedulerInternal {
   2438 
   2439         /**
   2440          * The current bucket heartbeat ordinal
   2441          */
   2442         public long currentHeartbeat() {
   2443             return getCurrentHeartbeat();
   2444         }
   2445 
   2446         /**
   2447          * Heartbeat ordinal at which the given standby bucket's jobs next become runnable
   2448          */
   2449         public long nextHeartbeatForBucket(int bucket) {
   2450             synchronized (mLock) {
   2451                 return mNextBucketHeartbeat[bucket];
   2452             }
   2453         }
   2454 
   2455         /**
   2456          * Heartbeat ordinal for the given app.  This is typically the heartbeat at which
   2457          * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run
   2458          * jobs in a long time is immediately runnable even if the app is bucketed into
   2459          * an infrequent time allocation.
   2460          */
   2461         public long baseHeartbeatForApp(String packageName, @UserIdInt int userId,
   2462                 final int appStandbyBucket) {
   2463             if (appStandbyBucket == 0 ||
   2464                     appStandbyBucket >= mConstants.STANDBY_BEATS.length) {
   2465                 // ACTIVE => everything can be run right away
   2466                 // NEVER => we won't run them anyway, so let them go in the future
   2467                 // as soon as the app enters normal use
   2468                 if (DEBUG_STANDBY) {
   2469                     Slog.v(TAG, "Base heartbeat forced ZERO for new job in "
   2470                             + packageName + "/" + userId);
   2471                 }
   2472                 return 0;
   2473             }
   2474 
   2475             final long baseHeartbeat = heartbeatWhenJobsLastRun(packageName, userId);
   2476             if (DEBUG_STANDBY) {
   2477                 Slog.v(TAG, "Base heartbeat " + baseHeartbeat + " for new job in "
   2478                         + packageName + "/" + userId);
   2479             }
   2480             return baseHeartbeat;
   2481         }
   2482 
   2483         public void noteJobStart(String packageName, int userId) {
   2484             synchronized (mLock) {
   2485                 setLastJobHeartbeatLocked(packageName, userId, mHeartbeat);
   2486             }
   2487         }
   2488 
   2489         /**
   2490          * Returns a list of all pending jobs. A running job is not considered pending. Periodic
   2491          * jobs are always considered pending.
   2492          */
   2493         @Override
   2494         public List<JobInfo> getSystemScheduledPendingJobs() {
   2495             synchronized (mLock) {
   2496                 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
   2497                 mJobs.forEachJob(Process.SYSTEM_UID, (job) -> {
   2498                     if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
   2499                         pendingJobs.add(job.getJob());
   2500                     }
   2501                 });
   2502                 return pendingJobs;
   2503             }
   2504         }
   2505 
   2506         @Override
   2507         public void cancelJobsForUid(int uid, String reason) {
   2508             JobSchedulerService.this.cancelJobsForUid(uid, reason);
   2509         }
   2510 
   2511         @Override
   2512         public void addBackingUpUid(int uid) {
   2513             synchronized (mLock) {
   2514                 // No need to actually do anything here, since for a full backup the
   2515                 // activity manager will kill the process which will kill the job (and
   2516                 // cause it to restart, but now it can't run).
   2517                 mBackingUpUids.put(uid, uid);
   2518             }
   2519         }
   2520 
   2521         @Override
   2522         public void removeBackingUpUid(int uid) {
   2523             synchronized (mLock) {
   2524                 mBackingUpUids.delete(uid);
   2525                 // If there are any jobs for this uid, we need to rebuild the pending list
   2526                 // in case they are now ready to run.
   2527                 if (mJobs.countJobsForUid(uid) > 0) {
   2528                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
   2529                 }
   2530             }
   2531         }
   2532 
   2533         @Override
   2534         public void clearAllBackingUpUids() {
   2535             synchronized (mLock) {
   2536                 if (mBackingUpUids.size() > 0) {
   2537                     mBackingUpUids.clear();
   2538                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
   2539                 }
   2540             }
   2541         }
   2542 
   2543         @Override
   2544         public void reportAppUsage(String packageName, int userId) {
   2545             JobSchedulerService.this.reportAppUsage(packageName, userId);
   2546         }
   2547 
   2548         @Override
   2549         public JobStorePersistStats getPersistStats() {
   2550             synchronized (mLock) {
   2551                 return new JobStorePersistStats(mJobs.getPersistStats());
   2552             }
   2553         }
   2554     }
   2555 
   2556     /**
   2557      * Tracking of app assignments to standby buckets
   2558      */
   2559     final class StandbyTracker extends AppIdleStateChangeListener {
   2560 
   2561         // AppIdleStateChangeListener interface for live updates
   2562 
   2563         @Override
   2564         public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
   2565                 boolean idle, int bucket, int reason) {
   2566             // QuotaController handles this now.
   2567         }
   2568 
   2569         @Override
   2570         public void onParoleStateChanged(boolean isParoleOn) {
   2571             if (DEBUG_STANDBY) {
   2572                 Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
   2573             }
   2574             mInParole = isParoleOn;
   2575         }
   2576 
   2577         @Override
   2578         public void onUserInteractionStarted(String packageName, int userId) {
   2579             final int uid = mLocalPM.getPackageUid(packageName,
   2580                     PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
   2581             if (uid < 0) {
   2582                 // Quietly ignore; the case is already logged elsewhere
   2583                 return;
   2584             }
   2585 
   2586             long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
   2587             if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) {
   2588                 // Too long ago, not worth logging
   2589                 sinceLast = 0L;
   2590             }
   2591             final DeferredJobCounter counter = new DeferredJobCounter();
   2592             synchronized (mLock) {
   2593                 mJobs.forEachJobForSourceUid(uid, counter);
   2594             }
   2595             if (counter.numDeferred() > 0 || sinceLast > 0) {
   2596                 BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService
   2597                         (BatteryStatsInternal.class);
   2598                 mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast);
   2599                 StatsLog.write_non_chained(StatsLog.DEFERRED_JOB_STATS_REPORTED, uid, null,
   2600                         counter.numDeferred(), sinceLast);
   2601             }
   2602         }
   2603     }
   2604 
   2605     static class DeferredJobCounter implements Consumer<JobStatus> {
   2606         private int mDeferred = 0;
   2607 
   2608         public int numDeferred() {
   2609             return mDeferred;
   2610         }
   2611 
   2612         @Override
   2613         public void accept(JobStatus job) {
   2614             if (job.getWhenStandbyDeferred() > 0) {
   2615                 mDeferred++;
   2616             }
   2617         }
   2618     }
   2619 
   2620     public static int standbyBucketToBucketIndex(int bucket) {
   2621         // Normalize AppStandby constants to indices into our bookkeeping
   2622         if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return NEVER_INDEX;
   2623         else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) return RARE_INDEX;
   2624         else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return FREQUENT_INDEX;
   2625         else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) return WORKING_INDEX;
   2626         else return ACTIVE_INDEX;
   2627     }
   2628 
   2629     // Static to support external callers
   2630     public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
   2631         UsageStatsManagerInternal usageStats = LocalServices.getService(
   2632                 UsageStatsManagerInternal.class);
   2633         int bucket = usageStats != null
   2634                 ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow)
   2635                 : 0;
   2636 
   2637         bucket = standbyBucketToBucketIndex(bucket);
   2638 
   2639         if (DEBUG_STANDBY) {
   2640             Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket);
   2641         }
   2642         return bucket;
   2643     }
   2644 
   2645     /**
   2646      * Binder stub trampoline implementation
   2647      */
   2648     final class JobSchedulerStub extends IJobScheduler.Stub {
   2649         /** Cache determination of whether a given app can persist jobs
   2650          * key is uid of the calling app; value is undetermined/true/false
   2651          */
   2652         private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
   2653 
   2654         // Enforce that only the app itself (or shared uid participant) can schedule a
   2655         // job that runs one of the app's services, as well as verifying that the
   2656         // named service properly requires the BIND_JOB_SERVICE permission
   2657         private void enforceValidJobRequest(int uid, JobInfo job) {
   2658             final IPackageManager pm = AppGlobals.getPackageManager();
   2659             final ComponentName service = job.getService();
   2660             try {
   2661                 ServiceInfo si = pm.getServiceInfo(service,
   2662                         PackageManager.MATCH_DIRECT_BOOT_AWARE
   2663                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
   2664                         UserHandle.getUserId(uid));
   2665                 if (si == null) {
   2666                     throw new IllegalArgumentException("No such service " + service);
   2667                 }
   2668                 if (si.applicationInfo.uid != uid) {
   2669                     throw new IllegalArgumentException("uid " + uid +
   2670                             " cannot schedule job in " + service.getPackageName());
   2671                 }
   2672                 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
   2673                     throw new IllegalArgumentException("Scheduled service " + service
   2674                             + " does not require android.permission.BIND_JOB_SERVICE permission");
   2675                 }
   2676             } catch (RemoteException e) {
   2677                 // Can't happen; the Package Manager is in this same process
   2678             }
   2679         }
   2680 
   2681         private boolean canPersistJobs(int pid, int uid) {
   2682             // If we get this far we're good to go; all we need to do now is check
   2683             // whether the app is allowed to persist its scheduled work.
   2684             final boolean canPersist;
   2685             synchronized (mPersistCache) {
   2686                 Boolean cached = mPersistCache.get(uid);
   2687                 if (cached != null) {
   2688                     canPersist = cached.booleanValue();
   2689                 } else {
   2690                     // Persisting jobs is tantamount to running at boot, so we permit
   2691                     // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
   2692                     // permission
   2693                     int result = getContext().checkPermission(
   2694                             android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
   2695                     canPersist = (result == PackageManager.PERMISSION_GRANTED);
   2696                     mPersistCache.put(uid, canPersist);
   2697                 }
   2698             }
   2699             return canPersist;
   2700         }
   2701 
   2702         private void validateJobFlags(JobInfo job, int callingUid) {
   2703             if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
   2704                 getContext().enforceCallingOrSelfPermission(
   2705                         android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
   2706             }
   2707             if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) {
   2708                 if (callingUid != Process.SYSTEM_UID) {
   2709                     throw new SecurityException("Job has invalid flags");
   2710                 }
   2711                 if (job.isPeriodic()) {
   2712                     Slog.wtf(TAG, "Periodic jobs mustn't have"
   2713                             + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
   2714                 }
   2715             }
   2716         }
   2717 
   2718         // IJobScheduler implementation
   2719         @Override
   2720         public int schedule(JobInfo job) throws RemoteException {
   2721             if (DEBUG) {
   2722                 Slog.d(TAG, "Scheduling job: " + job.toString());
   2723             }
   2724             final int pid = Binder.getCallingPid();
   2725             final int uid = Binder.getCallingUid();
   2726             final int userId = UserHandle.getUserId(uid);
   2727 
   2728             enforceValidJobRequest(uid, job);
   2729             if (job.isPersisted()) {
   2730                 if (!canPersistJobs(pid, uid)) {
   2731                     throw new IllegalArgumentException("Error: requested job be persisted without"
   2732                             + " holding RECEIVE_BOOT_COMPLETED permission.");
   2733                 }
   2734             }
   2735 
   2736             validateJobFlags(job, uid);
   2737 
   2738             long ident = Binder.clearCallingIdentity();
   2739             try {
   2740                 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
   2741                         null);
   2742             } finally {
   2743                 Binder.restoreCallingIdentity(ident);
   2744             }
   2745         }
   2746 
   2747         // IJobScheduler implementation
   2748         @Override
   2749         public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
   2750             if (DEBUG) {
   2751                 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
   2752             }
   2753             final int uid = Binder.getCallingUid();
   2754             final int userId = UserHandle.getUserId(uid);
   2755 
   2756             enforceValidJobRequest(uid, job);
   2757             if (job.isPersisted()) {
   2758                 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
   2759             }
   2760             if (work == null) {
   2761                 throw new NullPointerException("work is null");
   2762             }
   2763 
   2764             validateJobFlags(job, uid);
   2765 
   2766             long ident = Binder.clearCallingIdentity();
   2767             try {
   2768                 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
   2769                         null);
   2770             } finally {
   2771                 Binder.restoreCallingIdentity(ident);
   2772             }
   2773         }
   2774 
   2775         @Override
   2776         public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
   2777                 throws RemoteException {
   2778             final int callerUid = Binder.getCallingUid();
   2779             if (DEBUG) {
   2780                 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
   2781                         + " on behalf of " + packageName + "/");
   2782             }
   2783 
   2784             if (packageName == null) {
   2785                 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
   2786             }
   2787 
   2788             int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
   2789                     android.Manifest.permission.UPDATE_DEVICE_STATS);
   2790             if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
   2791                 throw new SecurityException("Caller uid " + callerUid
   2792                         + " not permitted to schedule jobs for other apps");
   2793             }
   2794 
   2795             validateJobFlags(job, callerUid);
   2796 
   2797             long ident = Binder.clearCallingIdentity();
   2798             try {
   2799                 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
   2800                         packageName, userId, tag);
   2801             } finally {
   2802                 Binder.restoreCallingIdentity(ident);
   2803             }
   2804         }
   2805 
   2806         @Override
   2807         public ParceledListSlice<JobInfo> getAllPendingJobs() throws RemoteException {
   2808             final int uid = Binder.getCallingUid();
   2809 
   2810             long ident = Binder.clearCallingIdentity();
   2811             try {
   2812                 return new ParceledListSlice<>(JobSchedulerService.this.getPendingJobs(uid));
   2813             } finally {
   2814                 Binder.restoreCallingIdentity(ident);
   2815             }
   2816         }
   2817 
   2818         @Override
   2819         public JobInfo getPendingJob(int jobId) throws RemoteException {
   2820             final int uid = Binder.getCallingUid();
   2821 
   2822             long ident = Binder.clearCallingIdentity();
   2823             try {
   2824                 return JobSchedulerService.this.getPendingJob(uid, jobId);
   2825             } finally {
   2826                 Binder.restoreCallingIdentity(ident);
   2827             }
   2828         }
   2829 
   2830         @Override
   2831         public void cancelAll() throws RemoteException {
   2832             final int uid = Binder.getCallingUid();
   2833             long ident = Binder.clearCallingIdentity();
   2834             try {
   2835                 JobSchedulerService.this.cancelJobsForUid(uid,
   2836                         "cancelAll() called by app, callingUid=" + uid);
   2837             } finally {
   2838                 Binder.restoreCallingIdentity(ident);
   2839             }
   2840         }
   2841 
   2842         @Override
   2843         public void cancel(int jobId) throws RemoteException {
   2844             final int uid = Binder.getCallingUid();
   2845 
   2846             long ident = Binder.clearCallingIdentity();
   2847             try {
   2848                 JobSchedulerService.this.cancelJob(uid, jobId, uid);
   2849             } finally {
   2850                 Binder.restoreCallingIdentity(ident);
   2851             }
   2852         }
   2853 
   2854         /**
   2855          * "dumpsys" infrastructure
   2856          */
   2857         @Override
   2858         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   2859             if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
   2860 
   2861             int filterUid = -1;
   2862             boolean proto = false;
   2863             if (!ArrayUtils.isEmpty(args)) {
   2864                 int opti = 0;
   2865                 while (opti < args.length) {
   2866                     String arg = args[opti];
   2867                     if ("-h".equals(arg)) {
   2868                         dumpHelp(pw);
   2869                         return;
   2870                     } else if ("-a".equals(arg)) {
   2871                         // Ignore, we always dump all.
   2872                     } else if ("--proto".equals(arg)) {
   2873                         proto = true;
   2874                     } else if (arg.length() > 0 && arg.charAt(0) == '-') {
   2875                         pw.println("Unknown option: " + arg);
   2876                         return;
   2877                     } else {
   2878                         break;
   2879                     }
   2880                     opti++;
   2881                 }
   2882                 if (opti < args.length) {
   2883                     String pkg = args[opti];
   2884                     try {
   2885                         filterUid = getContext().getPackageManager().getPackageUid(pkg,
   2886                                 PackageManager.MATCH_ANY_USER);
   2887                     } catch (NameNotFoundException ignored) {
   2888                         pw.println("Invalid package: " + pkg);
   2889                         return;
   2890                     }
   2891                 }
   2892             }
   2893 
   2894             final long identityToken = Binder.clearCallingIdentity();
   2895             try {
   2896                 if (proto) {
   2897                     JobSchedulerService.this.dumpInternalProto(fd, filterUid);
   2898                 } else {
   2899                     JobSchedulerService.this.dumpInternal(new IndentingPrintWriter(pw, "  "),
   2900                             filterUid);
   2901                 }
   2902             } finally {
   2903                 Binder.restoreCallingIdentity(identityToken);
   2904             }
   2905         }
   2906 
   2907         @Override
   2908         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
   2909                 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
   2910                 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
   2911                         this, in, out, err, args, callback, resultReceiver);
   2912         }
   2913 
   2914         /**
   2915          * <b>For internal system user only!</b>
   2916          * Returns a list of all currently-executing jobs.
   2917          */
   2918         @Override
   2919         public List<JobInfo> getStartedJobs() {
   2920             final int uid = Binder.getCallingUid();
   2921             if (uid != Process.SYSTEM_UID) {
   2922                 throw new SecurityException(
   2923                     "getStartedJobs() is system internal use only.");
   2924             }
   2925 
   2926             final ArrayList<JobInfo> runningJobs;
   2927 
   2928             synchronized (mLock) {
   2929                 runningJobs = new ArrayList<>(mActiveServices.size());
   2930                 for (JobServiceContext jsc : mActiveServices) {
   2931                     final JobStatus job = jsc.getRunningJobLocked();
   2932                     if (job != null) {
   2933                         runningJobs.add(job.getJob());
   2934                     }
   2935                 }
   2936             }
   2937 
   2938             return runningJobs;
   2939         }
   2940 
   2941         /**
   2942          * <b>For internal system user only!</b>
   2943          * Returns a snapshot of the state of all jobs known to the system.
   2944          *
   2945          * <p class="note">This is a slow operation, so it should be called sparingly.
   2946          */
   2947         @Override
   2948         public ParceledListSlice<JobSnapshot> getAllJobSnapshots() {
   2949             final int uid = Binder.getCallingUid();
   2950             if (uid != Process.SYSTEM_UID) {
   2951                 throw new SecurityException(
   2952                     "getAllJobSnapshots() is system internal use only.");
   2953             }
   2954             synchronized (mLock) {
   2955                 final ArrayList<JobSnapshot> snapshots = new ArrayList<>(mJobs.size());
   2956                 mJobs.forEachJob((job) -> snapshots.add(
   2957                         new JobSnapshot(job.getJob(), job.getSatisfiedConstraintFlags(),
   2958                                 isReadyToBeExecutedLocked(job))));
   2959                 return new ParceledListSlice<>(snapshots);
   2960             }
   2961         }
   2962     };
   2963 
   2964     // Shell command infrastructure: run the given job immediately
   2965     int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
   2966         if (DEBUG) {
   2967             Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
   2968                     + " " + jobId + " f=" + force);
   2969         }
   2970 
   2971         try {
   2972             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
   2973                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
   2974             if (uid < 0) {
   2975                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
   2976             }
   2977 
   2978             synchronized (mLock) {
   2979                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
   2980                 if (js == null) {
   2981                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
   2982                 }
   2983 
   2984                 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
   2985                 if (!js.isConstraintsSatisfied()) {
   2986                     js.overrideState = 0;
   2987                     return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
   2988                 }
   2989 
   2990                 queueReadyJobsForExecutionLocked();
   2991                 maybeRunPendingJobsLocked();
   2992             }
   2993         } catch (RemoteException e) {
   2994             // can't happen
   2995         }
   2996         return 0;
   2997     }
   2998 
   2999     // Shell command infrastructure: immediately timeout currently executing jobs
   3000     int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
   3001             boolean hasJobId, int jobId) {
   3002         if (DEBUG) {
   3003             Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
   3004         }
   3005 
   3006         synchronized (mLock) {
   3007             boolean foundSome = false;
   3008             for (int i=0; i<mActiveServices.size(); i++) {
   3009                 final JobServiceContext jc = mActiveServices.get(i);
   3010                 final JobStatus js = jc.getRunningJobLocked();
   3011                 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
   3012                     foundSome = true;
   3013                     pw.print("Timing out: ");
   3014                     js.printUniqueId(pw);
   3015                     pw.print(" ");
   3016                     pw.println(js.getServiceComponent().flattenToShortString());
   3017                 }
   3018             }
   3019             if (!foundSome) {
   3020                 pw.println("No matching executing jobs found.");
   3021             }
   3022         }
   3023         return 0;
   3024     }
   3025 
   3026     // Shell command infrastructure: cancel a scheduled job
   3027     int executeCancelCommand(PrintWriter pw, String pkgName, int userId,
   3028             boolean hasJobId, int jobId) {
   3029         if (DEBUG) {
   3030             Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
   3031         }
   3032 
   3033         int pkgUid = -1;
   3034         try {
   3035             IPackageManager pm = AppGlobals.getPackageManager();
   3036             pkgUid = pm.getPackageUid(pkgName, 0, userId);
   3037         } catch (RemoteException e) { /* can't happen */ }
   3038 
   3039         if (pkgUid < 0) {
   3040             pw.println("Package " + pkgName + " not found.");
   3041             return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
   3042         }
   3043 
   3044         if (!hasJobId) {
   3045             pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
   3046             if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) {
   3047                 pw.println("No matching jobs found.");
   3048             }
   3049         } else {
   3050             pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
   3051             if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) {
   3052                 pw.println("No matching job found.");
   3053             }
   3054         }
   3055 
   3056         return 0;
   3057     }
   3058 
   3059     void setMonitorBattery(boolean enabled) {
   3060         synchronized (mLock) {
   3061             if (mBatteryController != null) {
   3062                 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
   3063             }
   3064         }
   3065     }
   3066 
   3067     int getBatterySeq() {
   3068         synchronized (mLock) {
   3069             return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
   3070         }
   3071     }
   3072 
   3073     boolean getBatteryCharging() {
   3074         synchronized (mLock) {
   3075             return mBatteryController != null
   3076                     ? mBatteryController.getTracker().isOnStablePower() : false;
   3077         }
   3078     }
   3079 
   3080     boolean getBatteryNotLow() {
   3081         synchronized (mLock) {
   3082             return mBatteryController != null
   3083                     ? mBatteryController.getTracker().isBatteryNotLow() : false;
   3084         }
   3085     }
   3086 
   3087     int getStorageSeq() {
   3088         synchronized (mLock) {
   3089             return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
   3090         }
   3091     }
   3092 
   3093     boolean getStorageNotLow() {
   3094         synchronized (mLock) {
   3095             return mStorageController != null
   3096                     ? mStorageController.getTracker().isStorageNotLow() : false;
   3097         }
   3098     }
   3099 
   3100     long getCurrentHeartbeat() {
   3101         synchronized (mLock) {
   3102             return mHeartbeat;
   3103         }
   3104     }
   3105 
   3106     // Shell command infrastructure
   3107     int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
   3108         try {
   3109             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
   3110                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
   3111             if (uid < 0) {
   3112                 pw.print("unknown("); pw.print(pkgName); pw.println(")");
   3113                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
   3114             }
   3115 
   3116             synchronized (mLock) {
   3117                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
   3118                 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
   3119                 if (js == null) {
   3120                     pw.print("unknown("); UserHandle.formatUid(pw, uid);
   3121                     pw.print("/jid"); pw.print(jobId); pw.println(")");
   3122                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
   3123                 }
   3124 
   3125                 boolean printed = false;
   3126                 if (mPendingJobs.contains(js)) {
   3127                     pw.print("pending");
   3128                     printed = true;
   3129                 }
   3130                 if (isCurrentlyActiveLocked(js)) {
   3131                     if (printed) {
   3132                         pw.print(" ");
   3133                     }
   3134                     printed = true;
   3135                     pw.println("active");
   3136                 }
   3137                 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
   3138                     if (printed) {
   3139                         pw.print(" ");
   3140                     }
   3141                     printed = true;
   3142                     pw.println("user-stopped");
   3143                 }
   3144                 if (!ArrayUtils.contains(mStartedUsers, js.getSourceUserId())) {
   3145                     if (printed) {
   3146                         pw.print(" ");
   3147                     }
   3148                     printed = true;
   3149                     pw.println("source-user-stopped");
   3150                 }
   3151                 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
   3152                     if (printed) {
   3153                         pw.print(" ");
   3154                     }
   3155                     printed = true;
   3156                     pw.println("backing-up");
   3157                 }
   3158                 boolean componentPresent = false;
   3159                 try {
   3160                     componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
   3161                             js.getServiceComponent(),
   3162                             PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
   3163                             js.getUserId()) != null);
   3164                 } catch (RemoteException e) {
   3165                 }
   3166                 if (!componentPresent) {
   3167                     if (printed) {
   3168                         pw.print(" ");
   3169                     }
   3170                     printed = true;
   3171                     pw.println("no-component");
   3172                 }
   3173                 if (js.isReady()) {
   3174                     if (printed) {
   3175                         pw.print(" ");
   3176                     }
   3177                     printed = true;
   3178                     pw.println("ready");
   3179                 }
   3180                 if (!printed) {
   3181                     pw.print("waiting");
   3182                 }
   3183                 pw.println();
   3184             }
   3185         } catch (RemoteException e) {
   3186             // can't happen
   3187         }
   3188         return 0;
   3189     }
   3190 
   3191     // Shell command infrastructure
   3192     int executeHeartbeatCommand(PrintWriter pw, int numBeats) {
   3193         if (numBeats < 1) {
   3194             pw.println(getCurrentHeartbeat());
   3195             return 0;
   3196         }
   3197 
   3198         pw.print("Advancing standby heartbeat by ");
   3199         pw.println(numBeats);
   3200         synchronized (mLock) {
   3201             advanceHeartbeatLocked(numBeats);
   3202         }
   3203         return 0;
   3204     }
   3205 
   3206     void triggerDockState(boolean idleState) {
   3207         final Intent dockIntent;
   3208         if (idleState) {
   3209             dockIntent = new Intent(Intent.ACTION_DOCK_IDLE);
   3210         } else {
   3211             dockIntent = new Intent(Intent.ACTION_DOCK_ACTIVE);
   3212         }
   3213         dockIntent.setPackage("android");
   3214         dockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
   3215         getContext().sendBroadcastAsUser(dockIntent, UserHandle.ALL);
   3216     }
   3217 
   3218     static void dumpHelp(PrintWriter pw) {
   3219         pw.println("Job Scheduler (jobscheduler) dump options:");
   3220         pw.println("  [-h] [package] ...");
   3221         pw.println("    -h: print this help");
   3222         pw.println("  [package] is an optional package name to limit the output to.");
   3223     }
   3224 
   3225     /** Sort jobs by caller UID, then by Job ID. */
   3226     private static void sortJobs(List<JobStatus> jobs) {
   3227         Collections.sort(jobs, new Comparator<JobStatus>() {
   3228             @Override
   3229             public int compare(JobStatus o1, JobStatus o2) {
   3230                 int uid1 = o1.getUid();
   3231                 int uid2 = o2.getUid();
   3232                 int id1 = o1.getJobId();
   3233                 int id2 = o2.getJobId();
   3234                 if (uid1 != uid2) {
   3235                     return uid1 < uid2 ? -1 : 1;
   3236                 }
   3237                 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
   3238             }
   3239         });
   3240     }
   3241 
   3242     void dumpInternal(final IndentingPrintWriter pw, int filterUid) {
   3243         final int filterUidFinal = UserHandle.getAppId(filterUid);
   3244         final long now = sSystemClock.millis();
   3245         final long nowElapsed = sElapsedRealtimeClock.millis();
   3246         final long nowUptime = sUptimeMillisClock.millis();
   3247 
   3248         final Predicate<JobStatus> predicate = (js) -> {
   3249             return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
   3250                     || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
   3251         };
   3252         synchronized (mLock) {
   3253             mConstants.dump(pw);
   3254             for (StateController controller : mControllers) {
   3255                 pw.increaseIndent();
   3256                 controller.dumpConstants(pw);
   3257                 pw.decreaseIndent();
   3258             }
   3259             pw.println();
   3260 
   3261             pw.println("  Heartbeat:");
   3262             pw.print("    Current:    "); pw.println(mHeartbeat);
   3263             pw.println("    Next");
   3264             pw.print("      ACTIVE:   "); pw.println(mNextBucketHeartbeat[0]);
   3265             pw.print("      WORKING:  "); pw.println(mNextBucketHeartbeat[1]);
   3266             pw.print("      FREQUENT: "); pw.println(mNextBucketHeartbeat[2]);
   3267             pw.print("      RARE:     "); pw.println(mNextBucketHeartbeat[3]);
   3268             pw.print("    Last heartbeat: ");
   3269             TimeUtils.formatDuration(mLastHeartbeatTime, nowElapsed, pw);
   3270             pw.println();
   3271             pw.print("    Next heartbeat: ");
   3272             TimeUtils.formatDuration(mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME,
   3273                     nowElapsed, pw);
   3274             pw.println();
   3275             pw.print("    In parole?: ");
   3276             pw.print(mInParole);
   3277             pw.println();
   3278             pw.print("    In thermal throttling?: ");
   3279             pw.print(mThermalConstraint);
   3280             pw.println();
   3281             pw.println();
   3282 
   3283             pw.println("Started users: " + Arrays.toString(mStartedUsers));
   3284             pw.print("Registered ");
   3285             pw.print(mJobs.size());
   3286             pw.println(" jobs:");
   3287             if (mJobs.size() > 0) {
   3288                 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
   3289                 sortJobs(jobs);
   3290                 for (JobStatus job : jobs) {
   3291                     pw.print("  JOB #"); job.printUniqueId(pw); pw.print(": ");
   3292                     pw.println(job.toShortStringExceptUniqueId());
   3293 
   3294                     // Skip printing details if the caller requested a filter
   3295                     if (!predicate.test(job)) {
   3296                         continue;
   3297                     }
   3298 
   3299                     job.dump(pw, "    ", true, nowElapsed);
   3300                     pw.print("    Last run heartbeat: ");
   3301                     pw.print(heartbeatWhenJobsLastRun(job));
   3302                     pw.println();
   3303 
   3304                     pw.print("    Ready: ");
   3305                     pw.print(isReadyToBeExecutedLocked(job));
   3306                     pw.print(" (job=");
   3307                     pw.print(job.isReady());
   3308                     pw.print(" user=");
   3309                     pw.print(areUsersStartedLocked(job));
   3310                     pw.print(" !pending=");
   3311                     pw.print(!mPendingJobs.contains(job));
   3312                     pw.print(" !active=");
   3313                     pw.print(!isCurrentlyActiveLocked(job));
   3314                     pw.print(" !backingup=");
   3315                     pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
   3316                     pw.print(" comp=");
   3317                     boolean componentPresent = false;
   3318                     try {
   3319                         componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
   3320                                 job.getServiceComponent(),
   3321                                 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
   3322                                 job.getUserId()) != null);
   3323                     } catch (RemoteException e) {
   3324                     }
   3325                     pw.print(componentPresent);
   3326                     pw.println(")");
   3327                 }
   3328             } else {
   3329                 pw.println("  None.");
   3330             }
   3331             for (int i=0; i<mControllers.size(); i++) {
   3332                 pw.println();
   3333                 pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
   3334                 pw.increaseIndent();
   3335                 mControllers.get(i).dumpControllerStateLocked(pw, predicate);
   3336                 pw.decreaseIndent();
   3337             }
   3338             pw.println();
   3339             pw.println("Uid priority overrides:");
   3340             for (int i=0; i< mUidPriorityOverride.size(); i++) {
   3341                 int uid = mUidPriorityOverride.keyAt(i);
   3342                 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
   3343                     pw.print("  "); pw.print(UserHandle.formatUid(uid));
   3344                     pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
   3345                 }
   3346             }
   3347             if (mBackingUpUids.size() > 0) {
   3348                 pw.println();
   3349                 pw.println("Backing up uids:");
   3350                 boolean first = true;
   3351                 for (int i = 0; i < mBackingUpUids.size(); i++) {
   3352                     int uid = mBackingUpUids.keyAt(i);
   3353                     if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
   3354                         if (first) {
   3355                             pw.print("  ");
   3356                             first = false;
   3357                         } else {
   3358                             pw.print(", ");
   3359                         }
   3360                         pw.print(UserHandle.formatUid(uid));
   3361                     }
   3362                 }
   3363                 pw.println();
   3364             }
   3365             pw.println();
   3366             mJobPackageTracker.dump(pw, "", filterUidFinal);
   3367             pw.println();
   3368             if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
   3369                 pw.println();
   3370             }
   3371             pw.println("Pending queue:");
   3372             for (int i=0; i<mPendingJobs.size(); i++) {
   3373                 JobStatus job = mPendingJobs.get(i);
   3374                 pw.print("  Pending #"); pw.print(i); pw.print(": ");
   3375                 pw.println(job.toShortString());
   3376                 job.dump(pw, "    ", false, nowElapsed);
   3377                 int priority = evaluateJobPriorityLocked(job);
   3378                 pw.print("    Evaluated priority: ");
   3379                 pw.println(JobInfo.getPriorityString(priority));
   3380 
   3381                 pw.print("    Tag: "); pw.println(job.getTag());
   3382                 pw.print("    Enq: ");
   3383                 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
   3384                 pw.println();
   3385             }
   3386             pw.println();
   3387             pw.println("Active jobs:");
   3388             for (int i=0; i<mActiveServices.size(); i++) {
   3389                 JobServiceContext jsc = mActiveServices.get(i);
   3390                 pw.print("  Slot #"); pw.print(i); pw.print(": ");
   3391                 final JobStatus job = jsc.getRunningJobLocked();
   3392                 if (job == null) {
   3393                     if (jsc.mStoppedReason != null) {
   3394                         pw.print("inactive since ");
   3395                         TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
   3396                         pw.print(", stopped because: ");
   3397                         pw.println(jsc.mStoppedReason);
   3398                     } else {
   3399                         pw.println("inactive");
   3400                     }
   3401                     continue;
   3402                 } else {
   3403                     pw.println(job.toShortString());
   3404                     pw.print("    Running for: ");
   3405                     TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
   3406                     pw.print(", timeout at: ");
   3407                     TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
   3408                     pw.println();
   3409                     job.dump(pw, "    ", false, nowElapsed);
   3410                     int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
   3411                     pw.print("    Evaluated priority: ");
   3412                     pw.println(JobInfo.getPriorityString(priority));
   3413 
   3414                     pw.print("    Active at ");
   3415                     TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
   3416                     pw.print(", pending for ");
   3417                     TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
   3418                     pw.println();
   3419                 }
   3420             }
   3421             if (filterUid == -1) {
   3422                 pw.println();
   3423                 pw.print("mReadyToRock="); pw.println(mReadyToRock);
   3424                 pw.print("mReportedActive="); pw.println(mReportedActive);
   3425             }
   3426             pw.println();
   3427 
   3428             mConcurrencyManager.dumpLocked(pw, now, nowElapsed);
   3429 
   3430             pw.println();
   3431             pw.print("PersistStats: ");
   3432             pw.println(mJobs.getPersistStats());
   3433         }
   3434         pw.println();
   3435     }
   3436 
   3437     void dumpInternalProto(final FileDescriptor fd, int filterUid) {
   3438         ProtoOutputStream proto = new ProtoOutputStream(fd);
   3439         final int filterUidFinal = UserHandle.getAppId(filterUid);
   3440         final long now = sSystemClock.millis();
   3441         final long nowElapsed = sElapsedRealtimeClock.millis();
   3442         final long nowUptime = sUptimeMillisClock.millis();
   3443         final Predicate<JobStatus> predicate = (js) -> {
   3444             return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
   3445                     || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
   3446         };
   3447 
   3448         synchronized (mLock) {
   3449             final long settingsToken = proto.start(JobSchedulerServiceDumpProto.SETTINGS);
   3450             mConstants.dump(proto);
   3451             for (StateController controller : mControllers) {
   3452                 controller.dumpConstants(proto);
   3453             }
   3454             proto.end(settingsToken);
   3455 
   3456             proto.write(JobSchedulerServiceDumpProto.CURRENT_HEARTBEAT, mHeartbeat);
   3457             proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[0]);
   3458             proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[1]);
   3459             proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[2]);
   3460             proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[3]);
   3461             proto.write(JobSchedulerServiceDumpProto.LAST_HEARTBEAT_TIME_MILLIS,
   3462                     mLastHeartbeatTime - nowUptime);
   3463             proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT_TIME_MILLIS,
   3464                     mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME - nowUptime);
   3465             proto.write(JobSchedulerServiceDumpProto.IN_PAROLE, mInParole);
   3466             proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mThermalConstraint);
   3467 
   3468             for (int u : mStartedUsers) {
   3469                 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
   3470             }
   3471             if (mJobs.size() > 0) {
   3472                 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
   3473                 sortJobs(jobs);
   3474                 for (JobStatus job : jobs) {
   3475                     final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS);
   3476                     job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO);
   3477 
   3478                     // Skip printing details if the caller requested a filter
   3479                     if (!predicate.test(job)) {
   3480                         continue;
   3481                     }
   3482 
   3483                     job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
   3484 
   3485                     // isReadyToBeExecuted
   3486                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
   3487                             job.isReady());
   3488                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_USER_STARTED,
   3489                             areUsersStartedLocked(job));
   3490                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
   3491                             mPendingJobs.contains(job));
   3492                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
   3493                             isCurrentlyActiveLocked(job));
   3494                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
   3495                             mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
   3496                     boolean componentPresent = false;
   3497                     try {
   3498                         componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
   3499                                 job.getServiceComponent(),
   3500                                 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
   3501                                 job.getUserId()) != null);
   3502                     } catch (RemoteException e) {
   3503                     }
   3504                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_PRESENT,
   3505                             componentPresent);
   3506                     proto.write(RegisteredJob.LAST_RUN_HEARTBEAT, heartbeatWhenJobsLastRun(job));
   3507 
   3508                     proto.end(rjToken);
   3509                 }
   3510             }
   3511             for (StateController controller : mControllers) {
   3512                 controller.dumpControllerStateLocked(
   3513                         proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
   3514             }
   3515             for (int i=0; i< mUidPriorityOverride.size(); i++) {
   3516                 int uid = mUidPriorityOverride.keyAt(i);
   3517                 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
   3518                     long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
   3519                     proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
   3520                     proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
   3521                             mUidPriorityOverride.valueAt(i));
   3522                     proto.end(pToken);
   3523                 }
   3524             }
   3525             for (int i = 0; i < mBackingUpUids.size(); i++) {
   3526                 int uid = mBackingUpUids.keyAt(i);
   3527                 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
   3528                     proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
   3529                 }
   3530             }
   3531 
   3532             mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
   3533                     filterUidFinal);
   3534             mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
   3535                     filterUidFinal);
   3536 
   3537             for (JobStatus job : mPendingJobs) {
   3538                 final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
   3539 
   3540                 job.writeToShortProto(proto, PendingJob.INFO);
   3541                 job.dump(proto, PendingJob.DUMP, false, nowElapsed);
   3542                 proto.write(PendingJob.EVALUATED_PRIORITY, evaluateJobPriorityLocked(job));
   3543                 proto.write(PendingJob.ENQUEUED_DURATION_MS, nowUptime - job.madePending);
   3544 
   3545                 proto.end(pjToken);
   3546             }
   3547             for (JobServiceContext jsc : mActiveServices) {
   3548                 final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS);
   3549                 final JobStatus job = jsc.getRunningJobLocked();
   3550 
   3551                 if (job == null) {
   3552                     final long ijToken = proto.start(ActiveJob.INACTIVE);
   3553 
   3554                         proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
   3555                                 nowElapsed - jsc.mStoppedTime);
   3556                     if (jsc.mStoppedReason != null) {
   3557                         proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
   3558                                 jsc.mStoppedReason);
   3559                     }
   3560 
   3561                     proto.end(ijToken);
   3562                 } else {
   3563                     final long rjToken = proto.start(ActiveJob.RUNNING);
   3564 
   3565                     job.writeToShortProto(proto, ActiveJob.RunningJob.INFO);
   3566 
   3567                     proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS,
   3568                             nowElapsed - jsc.getExecutionStartTimeElapsed());
   3569                     proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS,
   3570                             jsc.getTimeoutElapsed() - nowElapsed);
   3571 
   3572                     job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
   3573 
   3574                     proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY,
   3575                             evaluateJobPriorityLocked(jsc.getRunningJobLocked()));
   3576 
   3577                     proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
   3578                             nowUptime - job.madeActive);
   3579                     proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS,
   3580                             job.madeActive - job.madePending);
   3581 
   3582                     proto.end(rjToken);
   3583                 }
   3584                 proto.end(ajToken);
   3585             }
   3586             if (filterUid == -1) {
   3587                 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
   3588                 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
   3589             }
   3590             mConcurrencyManager.dumpProtoLocked(proto,
   3591                     JobSchedulerServiceDumpProto.CONCURRENCY_MANAGER, now, nowElapsed);
   3592         }
   3593 
   3594         proto.flush();
   3595     }
   3596 }
   3597