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