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 java.io.FileDescriptor;
     23 import java.io.PrintWriter;
     24 import java.util.ArrayList;
     25 import java.util.Arrays;
     26 import java.util.Collections;
     27 import java.util.Comparator;
     28 import java.util.Iterator;
     29 import java.util.List;
     30 
     31 import android.app.Activity;
     32 import android.app.ActivityManager;
     33 import android.app.AppGlobals;
     34 import android.app.IUidObserver;
     35 import android.app.job.JobInfo;
     36 import android.app.job.JobParameters;
     37 import android.app.job.JobScheduler;
     38 import android.app.job.JobService;
     39 import android.app.job.IJobScheduler;
     40 import android.app.job.JobWorkItem;
     41 import android.content.BroadcastReceiver;
     42 import android.content.ComponentName;
     43 import android.content.ContentResolver;
     44 import android.content.Context;
     45 import android.content.Intent;
     46 import android.content.IntentFilter;
     47 import android.content.pm.IPackageManager;
     48 import android.content.pm.PackageManager;
     49 import android.content.pm.ServiceInfo;
     50 import android.content.pm.PackageManager.NameNotFoundException;
     51 import android.database.ContentObserver;
     52 import android.net.Uri;
     53 import android.os.BatteryStats;
     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.PowerManager;
     60 import android.os.RemoteException;
     61 import android.os.ResultReceiver;
     62 import android.os.ServiceManager;
     63 import android.os.ShellCallback;
     64 import android.os.SystemClock;
     65 import android.os.UserHandle;
     66 import android.os.UserManagerInternal;
     67 import android.provider.Settings;
     68 import android.util.KeyValueListParser;
     69 import android.util.Slog;
     70 import android.util.SparseArray;
     71 import android.util.SparseIntArray;
     72 import android.util.TimeUtils;
     73 
     74 import com.android.internal.app.IBatteryStats;
     75 import com.android.internal.app.procstats.ProcessStats;
     76 import com.android.internal.util.ArrayUtils;
     77 import com.android.internal.util.DumpUtils;
     78 import com.android.server.DeviceIdleController;
     79 import com.android.server.LocalServices;
     80 import com.android.server.job.JobStore.JobStatusFunctor;
     81 import com.android.server.job.controllers.AppIdleController;
     82 import com.android.server.job.controllers.BatteryController;
     83 import com.android.server.job.controllers.ConnectivityController;
     84 import com.android.server.job.controllers.ContentObserverController;
     85 import com.android.server.job.controllers.DeviceIdleJobsController;
     86 import com.android.server.job.controllers.IdleController;
     87 import com.android.server.job.controllers.JobStatus;
     88 import com.android.server.job.controllers.StateController;
     89 import com.android.server.job.controllers.StorageController;
     90 import com.android.server.job.controllers.TimeController;
     91 
     92 import libcore.util.EmptyArray;
     93 
     94 /**
     95  * Responsible for taking jobs representing work to be performed by a client app, and determining
     96  * based on the criteria specified when that job should be run against the client application's
     97  * endpoint.
     98  * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
     99  * about constraints, or the state of active jobs. It receives callbacks from the various
    100  * controllers and completed jobs and operates accordingly.
    101  *
    102  * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
    103  * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
    104  * @hide
    105  */
    106 public final class JobSchedulerService extends com.android.server.SystemService
    107         implements StateChangedListener, JobCompletedListener {
    108     static final String TAG = "JobSchedulerService";
    109     public static final boolean DEBUG = false;
    110 
    111     /** The maximum number of concurrent jobs we run at one time. */
    112     private static final int MAX_JOB_CONTEXTS_COUNT = 16;
    113     /** Enforce a per-app limit on scheduled jobs? */
    114     private static final boolean ENFORCE_MAX_JOBS = true;
    115     /** The maximum number of jobs that we allow an unprivileged app to schedule */
    116     private static final int MAX_JOBS_PER_APP = 100;
    117 
    118 
    119     /** Global local for all job scheduler state. */
    120     final Object mLock = new Object();
    121     /** Master list of jobs. */
    122     final JobStore mJobs;
    123     /** Tracking amount of time each package runs for. */
    124     final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
    125 
    126     static final int MSG_JOB_EXPIRED = 0;
    127     static final int MSG_CHECK_JOB = 1;
    128     static final int MSG_STOP_JOB = 2;
    129     static final int MSG_CHECK_JOB_GREEDY = 3;
    130 
    131     /**
    132      * Track Services that have currently active or pending jobs. The index is provided by
    133      * {@link JobStatus#getServiceToken()}
    134      */
    135     final List<JobServiceContext> mActiveServices = new ArrayList<>();
    136     /** List of controllers that will notify this service of updates to jobs. */
    137     List<StateController> mControllers;
    138     /** Need direct access to this for testing. */
    139     BatteryController mBatteryController;
    140     /** Need direct access to this for testing. */
    141     StorageController mStorageController;
    142     /**
    143      * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
    144      * when ready to execute them.
    145      */
    146     final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
    147 
    148     int[] mStartedUsers = EmptyArray.INT;
    149 
    150     final JobHandler mHandler;
    151     final JobSchedulerStub mJobSchedulerStub;
    152 
    153     IBatteryStats mBatteryStats;
    154     PowerManager mPowerManager;
    155     DeviceIdleController.LocalService mLocalDeviceIdleController;
    156 
    157     /**
    158      * Set to true once we are allowed to run third party apps.
    159      */
    160     boolean mReadyToRock;
    161 
    162     /**
    163      * What we last reported to DeviceIdleController about whether we are active.
    164      */
    165     boolean mReportedActive;
    166 
    167     /**
    168      * Current limit on the number of concurrent JobServiceContext entries we want to
    169      * keep actively running a job.
    170      */
    171     int mMaxActiveJobs = 1;
    172 
    173     /**
    174      * Which uids are currently in the foreground.
    175      */
    176     final SparseIntArray mUidPriorityOverride = new SparseIntArray();
    177 
    178     /**
    179      * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
    180      */
    181     final SparseIntArray mBackingUpUids = new SparseIntArray();
    182 
    183     // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
    184 
    185     /**
    186      * This array essentially stores the state of mActiveServices array.
    187      * The ith index stores the job present on the ith JobServiceContext.
    188      * We manipulate this array until we arrive at what jobs should be running on
    189      * what JobServiceContext.
    190      */
    191     JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
    192     /**
    193      * Indicates whether we need to act on this jobContext id
    194      */
    195     boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
    196     /**
    197      * The uid whose jobs we would like to assign to a context.
    198      */
    199     int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
    200 
    201     /**
    202      * All times are in milliseconds. These constants are kept synchronized with the system
    203      * global Settings. Any access to this class or its fields should be done while
    204      * holding the JobSchedulerService.mLock lock.
    205      */
    206     private final class Constants extends ContentObserver {
    207         // Key names stored in the settings value.
    208         private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
    209         private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
    210         private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
    211         private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
    212         private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
    213         private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
    214         private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
    215         private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
    216         private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
    217         private static final String KEY_FG_JOB_COUNT = "fg_job_count";
    218         private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
    219         private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
    220         private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
    221         private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
    222         private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
    223                 = "max_standard_reschedule_count";
    224         private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
    225         private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
    226         private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
    227 
    228         private static final int DEFAULT_MIN_IDLE_COUNT = 1;
    229         private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
    230         private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
    231         private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
    232         private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
    233         private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
    234         private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
    235         private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
    236         private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
    237         private static final int DEFAULT_FG_JOB_COUNT = 4;
    238         private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
    239         private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
    240         private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
    241         private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
    242         private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
    243         private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
    244         private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
    245         private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
    246 
    247         /**
    248          * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
    249          * early.
    250          */
    251         int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
    252         /**
    253          * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
    254          * things early.
    255          */
    256         int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
    257         /**
    258          * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
    259          * schedule things early.
    260          */
    261         int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
    262         /**
    263          * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
    264          * schedule things early.
    265          */
    266         int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
    267         /**
    268          * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
    269          * things early.  1 == Run connectivity jobs as soon as ready.
    270          */
    271         int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
    272         /**
    273          * Minimum # of content trigger jobs that must be ready in order to force the JMS to
    274          * schedule things early.
    275          */
    276         int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
    277         /**
    278          * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
    279          * running some work early.  This (and thus the other min counts) is now set to 1, to
    280          * prevent any batching at this level.  Since we now do batching through doze, that is
    281          * a much better mechanism.
    282          */
    283         int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
    284         /**
    285          * This is the job execution factor that is considered to be heavy use of the system.
    286          */
    287         float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
    288         /**
    289          * This is the job execution factor that is considered to be moderate use of the system.
    290          */
    291         float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
    292         /**
    293          * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
    294          */
    295         int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
    296         /**
    297          * The maximum number of background jobs we allow when the system is in a normal
    298          * memory state.
    299          */
    300         int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
    301         /**
    302          * The maximum number of background jobs we allow when the system is in a moderate
    303          * memory state.
    304          */
    305         int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
    306         /**
    307          * The maximum number of background jobs we allow when the system is in a low
    308          * memory state.
    309          */
    310         int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
    311         /**
    312          * The maximum number of background jobs we allow when the system is in a critical
    313          * memory state.
    314          */
    315         int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
    316         /**
    317          * The maximum number of times we allow a job to have itself rescheduled before
    318          * giving up on it, for standard jobs.
    319          */
    320         int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
    321         /**
    322          * The maximum number of times we allow a job to have itself rescheduled before
    323          * giving up on it, for jobs that are executing work.
    324          */
    325         int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
    326         /**
    327          * The minimum backoff time to allow for linear backoff.
    328          */
    329         long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
    330         /**
    331          * The minimum backoff time to allow for exponential backoff.
    332          */
    333         long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
    334 
    335         private ContentResolver mResolver;
    336         private final KeyValueListParser mParser = new KeyValueListParser(',');
    337 
    338         public Constants(Handler handler) {
    339             super(handler);
    340         }
    341 
    342         public void start(ContentResolver resolver) {
    343             mResolver = resolver;
    344             mResolver.registerContentObserver(Settings.Global.getUriFor(
    345                     Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
    346             updateConstants();
    347         }
    348 
    349         @Override
    350         public void onChange(boolean selfChange, Uri uri) {
    351             updateConstants();
    352         }
    353 
    354         private void updateConstants() {
    355             synchronized (mLock) {
    356                 try {
    357                     mParser.setString(Settings.Global.getString(mResolver,
    358                             Settings.Global.ALARM_MANAGER_CONSTANTS));
    359                 } catch (IllegalArgumentException e) {
    360                     // Failed to parse the settings string, log this and move on
    361                     // with defaults.
    362                     Slog.e(TAG, "Bad device idle settings", e);
    363                 }
    364 
    365                 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
    366                         DEFAULT_MIN_IDLE_COUNT);
    367                 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
    368                         DEFAULT_MIN_CHARGING_COUNT);
    369                 MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
    370                         DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
    371                 MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
    372                         DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
    373                 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
    374                         DEFAULT_MIN_CONNECTIVITY_COUNT);
    375                 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
    376                         DEFAULT_MIN_CONTENT_COUNT);
    377                 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
    378                         DEFAULT_MIN_READY_JOBS_COUNT);
    379                 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
    380                         DEFAULT_HEAVY_USE_FACTOR);
    381                 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
    382                         DEFAULT_MODERATE_USE_FACTOR);
    383                 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
    384                         DEFAULT_FG_JOB_COUNT);
    385                 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
    386                         DEFAULT_BG_NORMAL_JOB_COUNT);
    387                 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
    388                     BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
    389                 }
    390                 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
    391                         DEFAULT_BG_MODERATE_JOB_COUNT);
    392                 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
    393                     BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
    394                 }
    395                 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
    396                         DEFAULT_BG_LOW_JOB_COUNT);
    397                 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
    398                     BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
    399                 }
    400                 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
    401                         DEFAULT_BG_CRITICAL_JOB_COUNT);
    402                 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
    403                     BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
    404                 }
    405                 MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
    406                         DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
    407                 MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
    408                         DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
    409                 MIN_LINEAR_BACKOFF_TIME = mParser.getLong(KEY_MIN_LINEAR_BACKOFF_TIME,
    410                         DEFAULT_MIN_LINEAR_BACKOFF_TIME);
    411                 MIN_EXP_BACKOFF_TIME = mParser.getLong(KEY_MIN_EXP_BACKOFF_TIME,
    412                         DEFAULT_MIN_EXP_BACKOFF_TIME);
    413             }
    414         }
    415 
    416         void dump(PrintWriter pw) {
    417             pw.println("  Settings:");
    418 
    419             pw.print("    "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("=");
    420             pw.print(MIN_IDLE_COUNT); pw.println();
    421 
    422             pw.print("    "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("=");
    423             pw.print(MIN_CHARGING_COUNT); pw.println();
    424 
    425             pw.print("    "); pw.print(KEY_MIN_BATTERY_NOT_LOW_COUNT); pw.print("=");
    426             pw.print(MIN_BATTERY_NOT_LOW_COUNT); pw.println();
    427 
    428             pw.print("    "); pw.print(KEY_MIN_STORAGE_NOT_LOW_COUNT); pw.print("=");
    429             pw.print(MIN_STORAGE_NOT_LOW_COUNT); pw.println();
    430 
    431             pw.print("    "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("=");
    432             pw.print(MIN_CONNECTIVITY_COUNT); pw.println();
    433 
    434             pw.print("    "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("=");
    435             pw.print(MIN_CONTENT_COUNT); pw.println();
    436 
    437             pw.print("    "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("=");
    438             pw.print(MIN_READY_JOBS_COUNT); pw.println();
    439 
    440             pw.print("    "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("=");
    441             pw.print(HEAVY_USE_FACTOR); pw.println();
    442 
    443             pw.print("    "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("=");
    444             pw.print(MODERATE_USE_FACTOR); pw.println();
    445 
    446             pw.print("    "); pw.print(KEY_FG_JOB_COUNT); pw.print("=");
    447             pw.print(FG_JOB_COUNT); pw.println();
    448 
    449             pw.print("    "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("=");
    450             pw.print(BG_NORMAL_JOB_COUNT); pw.println();
    451 
    452             pw.print("    "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("=");
    453             pw.print(BG_MODERATE_JOB_COUNT); pw.println();
    454 
    455             pw.print("    "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("=");
    456             pw.print(BG_LOW_JOB_COUNT); pw.println();
    457 
    458             pw.print("    "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("=");
    459             pw.print(BG_CRITICAL_JOB_COUNT); pw.println();
    460 
    461             pw.print("    "); pw.print(KEY_MAX_STANDARD_RESCHEDULE_COUNT); pw.print("=");
    462             pw.print(MAX_STANDARD_RESCHEDULE_COUNT); pw.println();
    463 
    464             pw.print("    "); pw.print(KEY_MAX_WORK_RESCHEDULE_COUNT); pw.print("=");
    465             pw.print(MAX_WORK_RESCHEDULE_COUNT); pw.println();
    466 
    467             pw.print("    "); pw.print(KEY_MIN_LINEAR_BACKOFF_TIME); pw.print("=");
    468             pw.print(MIN_LINEAR_BACKOFF_TIME); pw.println();
    469 
    470             pw.print("    "); pw.print(KEY_MIN_EXP_BACKOFF_TIME); pw.print("=");
    471             pw.print(MIN_EXP_BACKOFF_TIME); pw.println();
    472         }
    473     }
    474 
    475     final Constants mConstants;
    476 
    477     static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
    478         if (o1.enqueueTime < o2.enqueueTime) {
    479             return -1;
    480         }
    481         return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
    482     };
    483 
    484     static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
    485         int where = Collections.binarySearch(array, newItem, comparator);
    486         if (where < 0) {
    487             where = ~where;
    488         }
    489         array.add(where, newItem);
    490     }
    491 
    492     /**
    493      * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
    494      * still clean up. On reinstall the package will have a new uid.
    495      */
    496     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    497         @Override
    498         public void onReceive(Context context, Intent intent) {
    499             final String action = intent.getAction();
    500             if (DEBUG) {
    501                 Slog.d(TAG, "Receieved: " + action);
    502             }
    503             if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
    504                 // Purge the app's jobs if the whole package was just disabled.  When this is
    505                 // the case the component name will be a bare package name.
    506                 final String pkgName = getPackageName(intent);
    507                 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
    508                 if (pkgName != null && pkgUid != -1) {
    509                     final String[] changedComponents = intent.getStringArrayExtra(
    510                             Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
    511                     if (changedComponents != null) {
    512                         for (String component : changedComponents) {
    513                             if (component.equals(pkgName)) {
    514                                 if (DEBUG) {
    515                                     Slog.d(TAG, "Package state change: " + pkgName);
    516                                 }
    517                                 try {
    518                                     final int userId = UserHandle.getUserId(pkgUid);
    519                                     IPackageManager pm = AppGlobals.getPackageManager();
    520                                     final int state = pm.getApplicationEnabledSetting(pkgName, userId);
    521                                     if (state == COMPONENT_ENABLED_STATE_DISABLED
    522                                             || state ==  COMPONENT_ENABLED_STATE_DISABLED_USER) {
    523                                         if (DEBUG) {
    524                                             Slog.d(TAG, "Removing jobs for package " + pkgName
    525                                                     + " in user " + userId);
    526                                         }
    527                                         cancelJobsForUid(pkgUid, "app package state changed");
    528                                     }
    529                                 } catch (RemoteException|IllegalArgumentException e) {
    530                                     /*
    531                                      * IllegalArgumentException means that the package doesn't exist.
    532                                      * This arises when PACKAGE_CHANGED broadcast delivery has lagged
    533                                      * behind outright uninstall, so by the time we try to act it's gone.
    534                                      * We don't need to act on this PACKAGE_CHANGED when this happens;
    535                                      * we'll get a PACKAGE_REMOVED later and clean up then.
    536                                      *
    537                                      * RemoteException can't actually happen; the package manager is
    538                                      * running in this same process.
    539                                      */
    540                                 }
    541                                 break;
    542                             }
    543                         }
    544                     }
    545                 } else {
    546                     Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
    547                 }
    548             } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
    549                 // If this is an outright uninstall rather than the first half of an
    550                 // app update sequence, cancel the jobs associated with the app.
    551                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
    552                     int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
    553                     if (DEBUG) {
    554                         Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
    555                     }
    556                     cancelJobsForUid(uidRemoved, "app uninstalled");
    557                 }
    558             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
    559                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
    560                 if (DEBUG) {
    561                     Slog.d(TAG, "Removing jobs for user: " + userId);
    562                 }
    563                 cancelJobsForUser(userId);
    564             } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
    565                 // Has this package scheduled any jobs, such that we will take action
    566                 // if it were to be force-stopped?
    567                 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
    568                 final String pkgName = intent.getData().getSchemeSpecificPart();
    569                 if (pkgUid != -1) {
    570                     List<JobStatus> jobsForUid;
    571                     synchronized (mLock) {
    572                         jobsForUid = mJobs.getJobsByUid(pkgUid);
    573                     }
    574                     for (int i = jobsForUid.size() - 1; i >= 0; i--) {
    575                         if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
    576                             if (DEBUG) {
    577                                 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
    578                                         + pkgUid + " has jobs");
    579                             }
    580                             setResultCode(Activity.RESULT_OK);
    581                             break;
    582                         }
    583                     }
    584                 }
    585             } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
    586                 // possible force-stop
    587                 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
    588                 final String pkgName = intent.getData().getSchemeSpecificPart();
    589                 if (pkgUid != -1) {
    590                     if (DEBUG) {
    591                         Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
    592                     }
    593                     cancelJobsForPackageAndUid(pkgName, pkgUid);
    594                 }
    595             }
    596         }
    597     };
    598 
    599     private String getPackageName(Intent intent) {
    600         Uri uri = intent.getData();
    601         String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
    602         return pkg;
    603     }
    604 
    605     final private IUidObserver mUidObserver = new IUidObserver.Stub() {
    606         @Override public void onUidStateChanged(int uid, int procState,
    607                 long procStateSeq) throws RemoteException {
    608             updateUidState(uid, procState);
    609         }
    610 
    611         @Override public void onUidGone(int uid, boolean disabled) throws RemoteException {
    612             updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
    613             if (disabled) {
    614                 cancelJobsForUid(uid, "uid gone");
    615             }
    616         }
    617 
    618         @Override public void onUidActive(int uid) throws RemoteException {
    619         }
    620 
    621         @Override public void onUidIdle(int uid, boolean disabled) throws RemoteException {
    622             if (disabled) {
    623                 cancelJobsForUid(uid, "app uid idle");
    624             }
    625         }
    626     };
    627 
    628     public Object getLock() {
    629         return mLock;
    630     }
    631 
    632     public JobStore getJobStore() {
    633         return mJobs;
    634     }
    635 
    636     @Override
    637     public void onStartUser(int userHandle) {
    638         mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
    639         // Let's kick any outstanding jobs for this user.
    640         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    641     }
    642 
    643     @Override
    644     public void onUnlockUser(int userHandle) {
    645         // Let's kick any outstanding jobs for this user.
    646         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    647     }
    648 
    649     @Override
    650     public void onStopUser(int userHandle) {
    651         mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
    652     }
    653 
    654     public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
    655             int userId, String tag) {
    656         try {
    657             if (ActivityManager.getService().isAppStartModeDisabled(uId,
    658                     job.getService().getPackageName())) {
    659                 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
    660                         + " -- package not allowed to start");
    661                 return JobScheduler.RESULT_FAILURE;
    662             }
    663         } catch (RemoteException e) {
    664         }
    665         synchronized (mLock) {
    666             final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
    667 
    668             if (work != null && toCancel != null) {
    669                 // Fast path: we are adding work to an existing job, and the JobInfo is not
    670                 // changing.  We can just directly enqueue this work in to the job.
    671                 if (toCancel.getJob().equals(job)) {
    672                     toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
    673                     return JobScheduler.RESULT_SUCCESS;
    674                 }
    675             }
    676 
    677             JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
    678             if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
    679             // Jobs on behalf of others don't apply to the per-app job cap
    680             if (ENFORCE_MAX_JOBS && packageName == null) {
    681                 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
    682                     Slog.w(TAG, "Too many jobs for uid " + uId);
    683                     throw new IllegalStateException("Apps may not schedule more than "
    684                                 + MAX_JOBS_PER_APP + " distinct jobs");
    685                 }
    686             }
    687 
    688             // This may throw a SecurityException.
    689             jobStatus.prepareLocked(ActivityManager.getService());
    690 
    691             if (toCancel != null) {
    692                 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
    693             }
    694             if (work != null) {
    695                 // If work has been supplied, enqueue it into the new job.
    696                 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
    697             }
    698             startTrackingJobLocked(jobStatus, toCancel);
    699 
    700             // If the job is immediately ready to run, then we can just immediately
    701             // put it in the pending list and try to schedule it.  This is especially
    702             // important for jobs with a 0 deadline constraint, since they will happen a fair
    703             // amount, we want to handle them as quickly as possible, and semantically we want to
    704             // make sure we have started holding the wake lock for the job before returning to
    705             // the caller.
    706             // If the job is not yet ready to run, there is nothing more to do -- we are
    707             // now just waiting for one of its controllers to change state and schedule
    708             // the job appropriately.
    709             if (isReadyToBeExecutedLocked(jobStatus)) {
    710                 // This is a new job, we can just immediately put it on the pending
    711                 // list and try to run it.
    712                 mJobPackageTracker.notePending(jobStatus);
    713                 addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
    714                 maybeRunPendingJobsLocked();
    715             }
    716         }
    717         return JobScheduler.RESULT_SUCCESS;
    718     }
    719 
    720     public List<JobInfo> getPendingJobs(int uid) {
    721         synchronized (mLock) {
    722             List<JobStatus> jobs = mJobs.getJobsByUid(uid);
    723             ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
    724             for (int i = jobs.size() - 1; i >= 0; i--) {
    725                 JobStatus job = jobs.get(i);
    726                 outList.add(job.getJob());
    727             }
    728             return outList;
    729         }
    730     }
    731 
    732     public JobInfo getPendingJob(int uid, int jobId) {
    733         synchronized (mLock) {
    734             List<JobStatus> jobs = mJobs.getJobsByUid(uid);
    735             for (int i = jobs.size() - 1; i >= 0; i--) {
    736                 JobStatus job = jobs.get(i);
    737                 if (job.getJobId() == jobId) {
    738                     return job.getJob();
    739                 }
    740             }
    741             return null;
    742         }
    743     }
    744 
    745     void cancelJobsForUser(int userHandle) {
    746         synchronized (mLock) {
    747             final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
    748             for (int i=0; i<jobsForUser.size(); i++) {
    749                 JobStatus toRemove = jobsForUser.get(i);
    750                 cancelJobImplLocked(toRemove, null, "user removed");
    751             }
    752         }
    753     }
    754 
    755     private void cancelJobsForNonExistentUsers() {
    756         UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
    757         synchronized (mLock) {
    758             mJobs.removeJobsOfNonUsers(umi.getUserIds());
    759         }
    760     }
    761 
    762     void cancelJobsForPackageAndUid(String pkgName, int uid) {
    763         synchronized (mLock) {
    764             final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
    765             for (int i = jobsForUid.size() - 1; i >= 0; i--) {
    766                 final JobStatus job = jobsForUid.get(i);
    767                 if (job.getSourcePackageName().equals(pkgName)) {
    768                     cancelJobImplLocked(job, null, "app force stopped");
    769                 }
    770             }
    771         }
    772     }
    773 
    774     /**
    775      * Entry point from client to cancel all jobs originating from their uid.
    776      * This will remove the job from the master list, and cancel the job if it was staged for
    777      * execution or being executed.
    778      * @param uid Uid to check against for removal of a job.
    779      *
    780      */
    781     public void cancelJobsForUid(int uid, String reason) {
    782         synchronized (mLock) {
    783             final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
    784             for (int i=0; i<jobsForUid.size(); i++) {
    785                 JobStatus toRemove = jobsForUid.get(i);
    786                 cancelJobImplLocked(toRemove, null, reason);
    787             }
    788         }
    789     }
    790 
    791     /**
    792      * Entry point from client to cancel the job corresponding to the jobId provided.
    793      * This will remove the job from the master list, and cancel the job if it was staged for
    794      * execution or being executed.
    795      * @param uid Uid of the calling client.
    796      * @param jobId Id of the job, provided at schedule-time.
    797      */
    798     public void cancelJob(int uid, int jobId) {
    799         JobStatus toCancel;
    800         synchronized (mLock) {
    801             toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
    802             if (toCancel != null) {
    803                 cancelJobImplLocked(toCancel, null, "cancel() called by app");
    804             }
    805         }
    806     }
    807 
    808     private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
    809         if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
    810         cancelled.unprepareLocked(ActivityManager.getService());
    811         stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
    812         // Remove from pending queue.
    813         if (mPendingJobs.remove(cancelled)) {
    814             mJobPackageTracker.noteNonpending(cancelled);
    815         }
    816         // Cancel if running.
    817         stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
    818         reportActiveLocked();
    819     }
    820 
    821     void updateUidState(int uid, int procState) {
    822         synchronized (mLock) {
    823             if (procState == ActivityManager.PROCESS_STATE_TOP) {
    824                 // Only use this if we are exactly the top app.  All others can live
    825                 // with just the foreground priority.  This means that persistent processes
    826                 // can never be the top app priority...  that is fine.
    827                 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
    828             } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
    829                 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
    830             } else {
    831                 mUidPriorityOverride.delete(uid);
    832             }
    833         }
    834     }
    835 
    836     @Override
    837     public void onDeviceIdleStateChanged(boolean deviceIdle) {
    838         synchronized (mLock) {
    839             if (deviceIdle) {
    840                 // When becoming idle, make sure no jobs are actively running,
    841                 // except those using the idle exemption flag.
    842                 for (int i=0; i<mActiveServices.size(); i++) {
    843                     JobServiceContext jsc = mActiveServices.get(i);
    844                     final JobStatus executing = jsc.getRunningJobLocked();
    845                     if (executing != null
    846                             && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
    847                         jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
    848                                 "cancelled due to doze");
    849                     }
    850                 }
    851             } else {
    852                 // When coming out of idle, allow thing to start back up.
    853                 if (mReadyToRock) {
    854                     if (mLocalDeviceIdleController != null) {
    855                         if (!mReportedActive) {
    856                             mReportedActive = true;
    857                             mLocalDeviceIdleController.setJobsActive(true);
    858                         }
    859                     }
    860                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    861                 }
    862             }
    863         }
    864     }
    865 
    866     void reportActiveLocked() {
    867         // active is true if pending queue contains jobs OR some job is running.
    868         boolean active = mPendingJobs.size() > 0;
    869         if (mPendingJobs.size() <= 0) {
    870             for (int i=0; i<mActiveServices.size(); i++) {
    871                 final JobServiceContext jsc = mActiveServices.get(i);
    872                 final JobStatus job = jsc.getRunningJobLocked();
    873                 if (job != null
    874                         && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
    875                         && !job.dozeWhitelisted) {
    876                     // We will report active if we have a job running and it is not an exception
    877                     // due to being in the foreground or whitelisted.
    878                     active = true;
    879                     break;
    880                 }
    881             }
    882         }
    883 
    884         if (mReportedActive != active) {
    885             mReportedActive = active;
    886             if (mLocalDeviceIdleController != null) {
    887                 mLocalDeviceIdleController.setJobsActive(active);
    888             }
    889         }
    890     }
    891 
    892     /**
    893      * Initializes the system service.
    894      * <p>
    895      * Subclasses must define a single argument constructor that accepts the context
    896      * and passes it to super.
    897      * </p>
    898      *
    899      * @param context The system server context.
    900      */
    901     public JobSchedulerService(Context context) {
    902         super(context);
    903         mHandler = new JobHandler(context.getMainLooper());
    904         mConstants = new Constants(mHandler);
    905         mJobSchedulerStub = new JobSchedulerStub();
    906         mJobs = JobStore.initAndGet(this);
    907 
    908         // Create the controllers.
    909         mControllers = new ArrayList<StateController>();
    910         mControllers.add(ConnectivityController.get(this));
    911         mControllers.add(TimeController.get(this));
    912         mControllers.add(IdleController.get(this));
    913         mBatteryController = BatteryController.get(this);
    914         mControllers.add(mBatteryController);
    915         mStorageController = StorageController.get(this);
    916         mControllers.add(mStorageController);
    917         mControllers.add(AppIdleController.get(this));
    918         mControllers.add(ContentObserverController.get(this));
    919         mControllers.add(DeviceIdleJobsController.get(this));
    920     }
    921 
    922     @Override
    923     public void onStart() {
    924         publishLocalService(JobSchedulerInternal.class, new LocalService());
    925         publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
    926     }
    927 
    928     @Override
    929     public void onBootPhase(int phase) {
    930         if (PHASE_SYSTEM_SERVICES_READY == phase) {
    931             mConstants.start(getContext().getContentResolver());
    932             // Register br for package removals and user removals.
    933             final IntentFilter filter = new IntentFilter();
    934             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    935             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    936             filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
    937             filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
    938             filter.addDataScheme("package");
    939             getContext().registerReceiverAsUser(
    940                     mBroadcastReceiver, UserHandle.ALL, filter, null, null);
    941             final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
    942             getContext().registerReceiverAsUser(
    943                     mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
    944             mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
    945             try {
    946                 ActivityManager.getService().registerUidObserver(mUidObserver,
    947                         ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
    948                         | ActivityManager.UID_OBSERVER_IDLE, ActivityManager.PROCESS_STATE_UNKNOWN,
    949                         null);
    950             } catch (RemoteException e) {
    951                 // ignored; both services live in system_server
    952             }
    953             // Remove any jobs that are not associated with any of the current users.
    954             cancelJobsForNonExistentUsers();
    955         } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
    956             synchronized (mLock) {
    957                 // Let's go!
    958                 mReadyToRock = true;
    959                 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
    960                         BatteryStats.SERVICE_NAME));
    961                 mLocalDeviceIdleController
    962                         = LocalServices.getService(DeviceIdleController.LocalService.class);
    963                 // Create the "runners".
    964                 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
    965                     mActiveServices.add(
    966                             new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
    967                                     getContext().getMainLooper()));
    968                 }
    969                 // Attach jobs to their controllers.
    970                 mJobs.forEachJob(new JobStatusFunctor() {
    971                     @Override
    972                     public void process(JobStatus job) {
    973                         for (int controller = 0; controller < mControllers.size(); controller++) {
    974                             final StateController sc = mControllers.get(controller);
    975                             sc.maybeStartTrackingJobLocked(job, null);
    976                         }
    977                     }
    978                 });
    979                 // GO GO GO!
    980                 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    981             }
    982         }
    983     }
    984 
    985     /**
    986      * Called when we have a job status object that we need to insert in our
    987      * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
    988      * about.
    989      */
    990     private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
    991         if (!jobStatus.isPreparedLocked()) {
    992             Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
    993         }
    994         jobStatus.enqueueTime = SystemClock.elapsedRealtime();
    995         final boolean update = mJobs.add(jobStatus);
    996         if (mReadyToRock) {
    997             for (int i = 0; i < mControllers.size(); i++) {
    998                 StateController controller = mControllers.get(i);
    999                 if (update) {
   1000                     controller.maybeStopTrackingJobLocked(jobStatus, null, true);
   1001                 }
   1002                 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
   1003             }
   1004         }
   1005     }
   1006 
   1007     /**
   1008      * Called when we want to remove a JobStatus object that we've finished executing. Returns the
   1009      * object removed.
   1010      */
   1011     private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
   1012             boolean writeBack) {
   1013         // Deal with any remaining work items in the old job.
   1014         jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
   1015 
   1016         // Remove from store as well as controllers.
   1017         final boolean removed = mJobs.remove(jobStatus, writeBack);
   1018         if (removed && mReadyToRock) {
   1019             for (int i=0; i<mControllers.size(); i++) {
   1020                 StateController controller = mControllers.get(i);
   1021                 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
   1022             }
   1023         }
   1024         return removed;
   1025     }
   1026 
   1027     private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
   1028         for (int i=0; i<mActiveServices.size(); i++) {
   1029             JobServiceContext jsc = mActiveServices.get(i);
   1030             final JobStatus executing = jsc.getRunningJobLocked();
   1031             if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
   1032                 jsc.cancelExecutingJobLocked(reason, debugReason);
   1033                 return true;
   1034             }
   1035         }
   1036         return false;
   1037     }
   1038 
   1039     /**
   1040      * @param job JobStatus we are querying against.
   1041      * @return Whether or not the job represented by the status object is currently being run or
   1042      * is pending.
   1043      */
   1044     private boolean isCurrentlyActiveLocked(JobStatus job) {
   1045         for (int i=0; i<mActiveServices.size(); i++) {
   1046             JobServiceContext serviceContext = mActiveServices.get(i);
   1047             final JobStatus running = serviceContext.getRunningJobLocked();
   1048             if (running != null && running.matches(job.getUid(), job.getJobId())) {
   1049                 return true;
   1050             }
   1051         }
   1052         return false;
   1053     }
   1054 
   1055     void noteJobsPending(List<JobStatus> jobs) {
   1056         for (int i = jobs.size() - 1; i >= 0; i--) {
   1057             JobStatus job = jobs.get(i);
   1058             mJobPackageTracker.notePending(job);
   1059         }
   1060     }
   1061 
   1062     void noteJobsNonpending(List<JobStatus> jobs) {
   1063         for (int i = jobs.size() - 1; i >= 0; i--) {
   1064             JobStatus job = jobs.get(i);
   1065             mJobPackageTracker.noteNonpending(job);
   1066         }
   1067     }
   1068 
   1069     /**
   1070      * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
   1071      * specify an override deadline on a failed job (the failed job will run even though it's not
   1072      * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
   1073      * ready job with {@link JobStatus#numFailures} > 0 will be executed.
   1074      *
   1075      * @param failureToReschedule Provided job status that we will reschedule.
   1076      * @return A newly instantiated JobStatus with the same constraints as the last job except
   1077      * with adjusted timing constraints.
   1078      *
   1079      * @see #maybeQueueReadyJobsForExecutionLocked
   1080      */
   1081     private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
   1082         final long elapsedNowMillis = SystemClock.elapsedRealtime();
   1083         final JobInfo job = failureToReschedule.getJob();
   1084 
   1085         final long initialBackoffMillis = job.getInitialBackoffMillis();
   1086         final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
   1087         long delayMillis;
   1088 
   1089         if (failureToReschedule.hasWorkLocked()) {
   1090             if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
   1091                 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
   1092                         + backoffAttempts + " > work limit "
   1093                         + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
   1094                 return null;
   1095             }
   1096         } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
   1097             Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
   1098                     + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
   1099             return null;
   1100         }
   1101 
   1102         switch (job.getBackoffPolicy()) {
   1103             case JobInfo.BACKOFF_POLICY_LINEAR: {
   1104                 long backoff = initialBackoffMillis;
   1105                 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
   1106                     backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
   1107                 }
   1108                 delayMillis = backoff * backoffAttempts;
   1109             } break;
   1110             default:
   1111                 if (DEBUG) {
   1112                     Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
   1113                 }
   1114             case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
   1115                 long backoff = initialBackoffMillis;
   1116                 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
   1117                     backoff = mConstants.MIN_EXP_BACKOFF_TIME;
   1118                 }
   1119                 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
   1120             } break;
   1121         }
   1122         delayMillis =
   1123                 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
   1124         JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
   1125                 JobStatus.NO_LATEST_RUNTIME, backoffAttempts);
   1126         for (int ic=0; ic<mControllers.size(); ic++) {
   1127             StateController controller = mControllers.get(ic);
   1128             controller.rescheduleForFailureLocked(newJob, failureToReschedule);
   1129         }
   1130         return newJob;
   1131     }
   1132 
   1133     /**
   1134      * Called after a periodic has executed so we can reschedule it. We take the last execution
   1135      * time of the job to be the time of completion (i.e. the time at which this function is
   1136      * called).
   1137      * This could be inaccurate b/c the job can run for as long as
   1138      * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
   1139      * to underscheduling at least, rather than if we had taken the last execution time to be the
   1140      * start of the execution.
   1141      * @return A new job representing the execution criteria for this instantiation of the
   1142      * recurring job.
   1143      */
   1144     private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
   1145         final long elapsedNow = SystemClock.elapsedRealtime();
   1146         // Compute how much of the period is remaining.
   1147         long runEarly = 0L;
   1148 
   1149         // If this periodic was rescheduled it won't have a deadline.
   1150         if (periodicToReschedule.hasDeadlineConstraint()) {
   1151             runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
   1152         }
   1153         long flex = periodicToReschedule.getJob().getFlexMillis();
   1154         long period = periodicToReschedule.getJob().getIntervalMillis();
   1155         long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
   1156         long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
   1157 
   1158         if (DEBUG) {
   1159             Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
   1160                     newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
   1161         }
   1162         return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
   1163                 newLatestRuntimeElapsed, 0 /* backoffAttempt */);
   1164     }
   1165 
   1166     // JobCompletedListener implementations.
   1167 
   1168     /**
   1169      * A job just finished executing. We fetch the
   1170      * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
   1171      * whether we want to reschedule we readd it to the controllers.
   1172      * @param jobStatus Completed job.
   1173      * @param needsReschedule Whether the implementing class should reschedule this job.
   1174      */
   1175     @Override
   1176     public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
   1177         if (DEBUG) {
   1178             Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
   1179         }
   1180 
   1181         // If the job wants to be rescheduled, we first need to make the next upcoming
   1182         // job so we can transfer any appropriate state over from the previous job when
   1183         // we stop it.
   1184         final JobStatus rescheduledJob = needsReschedule
   1185                 ? getRescheduleJobForFailureLocked(jobStatus) : null;
   1186 
   1187         // Do not write back immediately if this is a periodic job. The job may get lost if system
   1188         // shuts down before it is added back.
   1189         if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
   1190             if (DEBUG) {
   1191                 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
   1192             }
   1193             // We still want to check for jobs to execute, because this job may have
   1194             // scheduled a new job under the same job id, and now we can run it.
   1195             mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
   1196             return;
   1197         }
   1198 
   1199         if (rescheduledJob != null) {
   1200             try {
   1201                 rescheduledJob.prepareLocked(ActivityManager.getService());
   1202             } catch (SecurityException e) {
   1203                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
   1204             }
   1205             startTrackingJobLocked(rescheduledJob, jobStatus);
   1206         } else if (jobStatus.getJob().isPeriodic()) {
   1207             JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
   1208             try {
   1209                 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
   1210             } catch (SecurityException e) {
   1211                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
   1212             }
   1213             startTrackingJobLocked(rescheduledPeriodic, jobStatus);
   1214         }
   1215         jobStatus.unprepareLocked(ActivityManager.getService());
   1216         reportActiveLocked();
   1217         mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
   1218     }
   1219 
   1220     // StateChangedListener implementations.
   1221 
   1222     /**
   1223      * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
   1224      * some controller's state has changed, so as to run through the list of jobs and start/stop
   1225      * any that are eligible.
   1226      */
   1227     @Override
   1228     public void onControllerStateChanged() {
   1229         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
   1230     }
   1231 
   1232     @Override
   1233     public void onRunJobNow(JobStatus jobStatus) {
   1234         mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
   1235     }
   1236 
   1237     final private class JobHandler extends Handler {
   1238 
   1239         public JobHandler(Looper looper) {
   1240             super(looper);
   1241         }
   1242 
   1243         @Override
   1244         public void handleMessage(Message message) {
   1245             synchronized (mLock) {
   1246                 if (!mReadyToRock) {
   1247                     return;
   1248                 }
   1249                 switch (message.what) {
   1250                     case MSG_JOB_EXPIRED: {
   1251                         JobStatus runNow = (JobStatus) message.obj;
   1252                         // runNow can be null, which is a controller's way of indicating that its
   1253                         // state is such that all ready jobs should be run immediately.
   1254                         if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
   1255                             mJobPackageTracker.notePending(runNow);
   1256                             addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
   1257                         } else {
   1258                             queueReadyJobsForExecutionLocked();
   1259                         }
   1260                     } break;
   1261                     case MSG_CHECK_JOB:
   1262                         if (mReportedActive) {
   1263                             // if jobs are currently being run, queue all ready jobs for execution.
   1264                             queueReadyJobsForExecutionLocked();
   1265                         } else {
   1266                             // Check the list of jobs and run some of them if we feel inclined.
   1267                             maybeQueueReadyJobsForExecutionLocked();
   1268                         }
   1269                         break;
   1270                     case MSG_CHECK_JOB_GREEDY:
   1271                         queueReadyJobsForExecutionLocked();
   1272                         break;
   1273                     case MSG_STOP_JOB:
   1274                         cancelJobImplLocked((JobStatus) message.obj, null,
   1275                                 "app no longer allowed to run");
   1276                         break;
   1277                 }
   1278                 maybeRunPendingJobsLocked();
   1279                 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
   1280                 removeMessages(MSG_CHECK_JOB);
   1281             }
   1282         }
   1283     }
   1284 
   1285     private void stopNonReadyActiveJobsLocked() {
   1286         for (int i=0; i<mActiveServices.size(); i++) {
   1287             JobServiceContext serviceContext = mActiveServices.get(i);
   1288             final JobStatus running = serviceContext.getRunningJobLocked();
   1289             if (running != null && !running.isReady()) {
   1290                 serviceContext.cancelExecutingJobLocked(
   1291                         JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
   1292                         "cancelled due to unsatisfied constraints");
   1293             }
   1294         }
   1295     }
   1296 
   1297     /**
   1298      * Run through list of jobs and execute all possible - at least one is expired so we do
   1299      * as many as we can.
   1300      */
   1301     private void queueReadyJobsForExecutionLocked() {
   1302         if (DEBUG) {
   1303             Slog.d(TAG, "queuing all ready jobs for execution:");
   1304         }
   1305         noteJobsNonpending(mPendingJobs);
   1306         mPendingJobs.clear();
   1307         stopNonReadyActiveJobsLocked();
   1308         mJobs.forEachJob(mReadyQueueFunctor);
   1309         mReadyQueueFunctor.postProcess();
   1310 
   1311         if (DEBUG) {
   1312             final int queuedJobs = mPendingJobs.size();
   1313             if (queuedJobs == 0) {
   1314                 Slog.d(TAG, "No jobs pending.");
   1315             } else {
   1316                 Slog.d(TAG, queuedJobs + " jobs queued.");
   1317             }
   1318         }
   1319     }
   1320 
   1321     final class ReadyJobQueueFunctor implements JobStatusFunctor {
   1322         ArrayList<JobStatus> newReadyJobs;
   1323 
   1324         @Override
   1325         public void process(JobStatus job) {
   1326             if (isReadyToBeExecutedLocked(job)) {
   1327                 if (DEBUG) {
   1328                     Slog.d(TAG, "    queued " + job.toShortString());
   1329                 }
   1330                 if (newReadyJobs == null) {
   1331                     newReadyJobs = new ArrayList<JobStatus>();
   1332                 }
   1333                 newReadyJobs.add(job);
   1334             }
   1335         }
   1336 
   1337         public void postProcess() {
   1338             if (newReadyJobs != null) {
   1339                 noteJobsPending(newReadyJobs);
   1340                 mPendingJobs.addAll(newReadyJobs);
   1341                 if (mPendingJobs.size() > 1) {
   1342                     mPendingJobs.sort(mEnqueueTimeComparator);
   1343                 }
   1344             }
   1345             newReadyJobs = null;
   1346         }
   1347     }
   1348     private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
   1349 
   1350     /**
   1351      * The state of at least one job has changed. Here is where we could enforce various
   1352      * policies on when we want to execute jobs.
   1353      * Right now the policy is such:
   1354      * If >1 of the ready jobs is idle mode we send all of them off
   1355      * if more than 2 network connectivity jobs are ready we send them all off.
   1356      * If more than 4 jobs total are ready we send them all off.
   1357      * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
   1358      */
   1359     final class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
   1360         int chargingCount;
   1361         int batteryNotLowCount;
   1362         int storageNotLowCount;
   1363         int idleCount;
   1364         int backoffCount;
   1365         int connectivityCount;
   1366         int contentCount;
   1367         List<JobStatus> runnableJobs;
   1368 
   1369         public MaybeReadyJobQueueFunctor() {
   1370             reset();
   1371         }
   1372 
   1373         // Functor method invoked for each job via JobStore.forEachJob()
   1374         @Override
   1375         public void process(JobStatus job) {
   1376             if (isReadyToBeExecutedLocked(job)) {
   1377                 try {
   1378                     if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
   1379                             job.getJob().getService().getPackageName())) {
   1380                         Slog.w(TAG, "Aborting job " + job.getUid() + ":"
   1381                                 + job.getJob().toString() + " -- package not allowed to start");
   1382                         mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
   1383                         return;
   1384                     }
   1385                 } catch (RemoteException e) {
   1386                 }
   1387                 if (job.getNumFailures() > 0) {
   1388                     backoffCount++;
   1389                 }
   1390                 if (job.hasIdleConstraint()) {
   1391                     idleCount++;
   1392                 }
   1393                 if (job.hasConnectivityConstraint()) {
   1394                     connectivityCount++;
   1395                 }
   1396                 if (job.hasChargingConstraint()) {
   1397                     chargingCount++;
   1398                 }
   1399                 if (job.hasBatteryNotLowConstraint()) {
   1400                     batteryNotLowCount++;
   1401                 }
   1402                 if (job.hasStorageNotLowConstraint()) {
   1403                     storageNotLowCount++;
   1404                 }
   1405                 if (job.hasContentTriggerConstraint()) {
   1406                     contentCount++;
   1407                 }
   1408                 if (runnableJobs == null) {
   1409                     runnableJobs = new ArrayList<>();
   1410                 }
   1411                 runnableJobs.add(job);
   1412             }
   1413         }
   1414 
   1415         public void postProcess() {
   1416             if (backoffCount > 0 ||
   1417                     idleCount >= mConstants.MIN_IDLE_COUNT ||
   1418                     connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
   1419                     chargingCount >= mConstants.MIN_CHARGING_COUNT ||
   1420                     batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
   1421                     storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
   1422                     contentCount >= mConstants.MIN_CONTENT_COUNT ||
   1423                     (runnableJobs != null
   1424                             && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
   1425                 if (DEBUG) {
   1426                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
   1427                 }
   1428                 noteJobsPending(runnableJobs);
   1429                 mPendingJobs.addAll(runnableJobs);
   1430                 if (mPendingJobs.size() > 1) {
   1431                     mPendingJobs.sort(mEnqueueTimeComparator);
   1432                 }
   1433             } else {
   1434                 if (DEBUG) {
   1435                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
   1436                 }
   1437             }
   1438 
   1439             // Be ready for next time
   1440             reset();
   1441         }
   1442 
   1443         private void reset() {
   1444             chargingCount = 0;
   1445             idleCount =  0;
   1446             backoffCount = 0;
   1447             connectivityCount = 0;
   1448             batteryNotLowCount = 0;
   1449             storageNotLowCount = 0;
   1450             contentCount = 0;
   1451             runnableJobs = null;
   1452         }
   1453     }
   1454     private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
   1455 
   1456     private void maybeQueueReadyJobsForExecutionLocked() {
   1457         if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
   1458 
   1459         noteJobsNonpending(mPendingJobs);
   1460         mPendingJobs.clear();
   1461         stopNonReadyActiveJobsLocked();
   1462         mJobs.forEachJob(mMaybeQueueFunctor);
   1463         mMaybeQueueFunctor.postProcess();
   1464     }
   1465 
   1466     /**
   1467      * Criteria for moving a job into the pending queue:
   1468      *      - It's ready.
   1469      *      - It's not pending.
   1470      *      - It's not already running on a JSC.
   1471      *      - The user that requested the job is running.
   1472      *      - The component is enabled and runnable.
   1473      */
   1474     private boolean isReadyToBeExecutedLocked(JobStatus job) {
   1475         final boolean jobReady = job.isReady();
   1476 
   1477         if (DEBUG) {
   1478             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
   1479                     + " ready=" + jobReady);
   1480         }
   1481 
   1482         // This is a condition that is very likely to be false (most jobs that are
   1483         // scheduled are sitting there, not ready yet) and very cheap to check (just
   1484         // a few conditions on data in JobStatus).
   1485         if (!jobReady) {
   1486             return false;
   1487         }
   1488 
   1489         final boolean jobExists = mJobs.containsJob(job);
   1490 
   1491         final int userId = job.getUserId();
   1492         final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
   1493 
   1494         if (DEBUG) {
   1495             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
   1496                     + " exists=" + jobExists + " userStarted=" + userStarted);
   1497         }
   1498 
   1499         // These are also fairly cheap to check, though they typically will not
   1500         // be conditions we fail.
   1501         if (!jobExists || !userStarted) {
   1502             return false;
   1503         }
   1504 
   1505         final boolean jobPending = mPendingJobs.contains(job);
   1506         final boolean jobActive = isCurrentlyActiveLocked(job);
   1507 
   1508         if (DEBUG) {
   1509             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
   1510                     + " pending=" + jobPending + " active=" + jobActive);
   1511         }
   1512 
   1513         // These can be a little more expensive (especially jobActive, since we need to
   1514         // go through the array of all potentially active jobs), so we are doing them
   1515         // later...  but still before checking with the package manager!
   1516         if (jobPending || jobActive) {
   1517             return false;
   1518         }
   1519 
   1520         final boolean componentPresent;
   1521         try {
   1522             componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
   1523                     job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
   1524                     userId) != null);
   1525         } catch (RemoteException e) {
   1526             throw e.rethrowAsRuntimeException();
   1527         }
   1528 
   1529         if (DEBUG) {
   1530             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
   1531                     + " componentPresent=" + componentPresent);
   1532         }
   1533 
   1534         // Everything else checked out so far, so this is the final yes/no check
   1535         return componentPresent;
   1536     }
   1537 
   1538     /**
   1539      * Reconcile jobs in the pending queue against available execution contexts.
   1540      * A controller can force a job into the pending queue even if it's already running, but
   1541      * here is where we decide whether to actually execute it.
   1542      */
   1543     private void maybeRunPendingJobsLocked() {
   1544         if (DEBUG) {
   1545             Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
   1546         }
   1547         assignJobsToContextsLocked();
   1548         reportActiveLocked();
   1549     }
   1550 
   1551     private int adjustJobPriority(int curPriority, JobStatus job) {
   1552         if (curPriority < JobInfo.PRIORITY_TOP_APP) {
   1553             float factor = mJobPackageTracker.getLoadFactor(job);
   1554             if (factor >= mConstants.HEAVY_USE_FACTOR) {
   1555                 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
   1556             } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
   1557                 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
   1558             }
   1559         }
   1560         return curPriority;
   1561     }
   1562 
   1563     private int evaluateJobPriorityLocked(JobStatus job) {
   1564         int priority = job.getPriority();
   1565         if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
   1566             return adjustJobPriority(priority, job);
   1567         }
   1568         int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
   1569         if (override != 0) {
   1570             return adjustJobPriority(override, job);
   1571         }
   1572         return adjustJobPriority(priority, job);
   1573     }
   1574 
   1575     /**
   1576      * Takes jobs from pending queue and runs them on available contexts.
   1577      * If no contexts are available, preempts lower priority jobs to
   1578      * run higher priority ones.
   1579      * Lock on mJobs before calling this function.
   1580      */
   1581     private void assignJobsToContextsLocked() {
   1582         if (DEBUG) {
   1583             Slog.d(TAG, printPendingQueue());
   1584         }
   1585 
   1586         int memLevel;
   1587         try {
   1588             memLevel = ActivityManager.getService().getMemoryTrimLevel();
   1589         } catch (RemoteException e) {
   1590             memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
   1591         }
   1592         switch (memLevel) {
   1593             case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
   1594                 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
   1595                 break;
   1596             case ProcessStats.ADJ_MEM_FACTOR_LOW:
   1597                 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
   1598                 break;
   1599             case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
   1600                 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
   1601                 break;
   1602             default:
   1603                 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
   1604                 break;
   1605         }
   1606 
   1607         JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
   1608         boolean[] act = mTmpAssignAct;
   1609         int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
   1610         int numActive = 0;
   1611         int numForeground = 0;
   1612         for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
   1613             final JobServiceContext js = mActiveServices.get(i);
   1614             final JobStatus status = js.getRunningJobLocked();
   1615             if ((contextIdToJobMap[i] = status) != null) {
   1616                 numActive++;
   1617                 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
   1618                     numForeground++;
   1619                 }
   1620             }
   1621             act[i] = false;
   1622             preferredUidForContext[i] = js.getPreferredUid();
   1623         }
   1624         if (DEBUG) {
   1625             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
   1626         }
   1627         for (int i=0; i<mPendingJobs.size(); i++) {
   1628             JobStatus nextPending = mPendingJobs.get(i);
   1629 
   1630             // If job is already running, go to next job.
   1631             int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
   1632             if (jobRunningContext != -1) {
   1633                 continue;
   1634             }
   1635 
   1636             final int priority = evaluateJobPriorityLocked(nextPending);
   1637             nextPending.lastEvaluatedPriority = priority;
   1638 
   1639             // Find a context for nextPending. The context should be available OR
   1640             // it should have lowest priority among all running jobs
   1641             // (sharing the same Uid as nextPending)
   1642             int minPriority = Integer.MAX_VALUE;
   1643             int minPriorityContextId = -1;
   1644             for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
   1645                 JobStatus job = contextIdToJobMap[j];
   1646                 int preferredUid = preferredUidForContext[j];
   1647                 if (job == null) {
   1648                     if ((numActive < mMaxActiveJobs ||
   1649                             (priority >= JobInfo.PRIORITY_TOP_APP &&
   1650                                     numForeground < mConstants.FG_JOB_COUNT)) &&
   1651                             (preferredUid == nextPending.getUid() ||
   1652                                     preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
   1653                         // This slot is free, and we haven't yet hit the limit on
   1654                         // concurrent jobs...  we can just throw the job in to here.
   1655                         minPriorityContextId = j;
   1656                         break;
   1657                     }
   1658                     // No job on this context, but nextPending can't run here because
   1659                     // the context has a preferred Uid or we have reached the limit on
   1660                     // concurrent jobs.
   1661                     continue;
   1662                 }
   1663                 if (job.getUid() != nextPending.getUid()) {
   1664                     continue;
   1665                 }
   1666                 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
   1667                     continue;
   1668                 }
   1669                 if (minPriority > nextPending.lastEvaluatedPriority) {
   1670                     minPriority = nextPending.lastEvaluatedPriority;
   1671                     minPriorityContextId = j;
   1672                 }
   1673             }
   1674             if (minPriorityContextId != -1) {
   1675                 contextIdToJobMap[minPriorityContextId] = nextPending;
   1676                 act[minPriorityContextId] = true;
   1677                 numActive++;
   1678                 if (priority >= JobInfo.PRIORITY_TOP_APP) {
   1679                     numForeground++;
   1680                 }
   1681             }
   1682         }
   1683         if (DEBUG) {
   1684             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
   1685         }
   1686         mJobPackageTracker.noteConcurrency(numActive, numForeground);
   1687         for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
   1688             boolean preservePreferredUid = false;
   1689             if (act[i]) {
   1690                 JobStatus js = mActiveServices.get(i).getRunningJobLocked();
   1691                 if (js != null) {
   1692                     if (DEBUG) {
   1693                         Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJobLocked());
   1694                     }
   1695                     // preferredUid will be set to uid of currently running job.
   1696                     mActiveServices.get(i).preemptExecutingJobLocked();
   1697                     preservePreferredUid = true;
   1698                 } else {
   1699                     final JobStatus pendingJob = contextIdToJobMap[i];
   1700                     if (DEBUG) {
   1701                         Slog.d(TAG, "About to run job on context "
   1702                                 + String.valueOf(i) + ", job: " + pendingJob);
   1703                     }
   1704                     for (int ic=0; ic<mControllers.size(); ic++) {
   1705                         mControllers.get(ic).prepareForExecutionLocked(pendingJob);
   1706                     }
   1707                     if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
   1708                         Slog.d(TAG, "Error executing " + pendingJob);
   1709                     }
   1710                     if (mPendingJobs.remove(pendingJob)) {
   1711                         mJobPackageTracker.noteNonpending(pendingJob);
   1712                     }
   1713                 }
   1714             }
   1715             if (!preservePreferredUid) {
   1716                 mActiveServices.get(i).clearPreferredUid();
   1717             }
   1718         }
   1719     }
   1720 
   1721     int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
   1722         for (int i=0; i<map.length; i++) {
   1723             if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
   1724                 return i;
   1725             }
   1726         }
   1727         return -1;
   1728     }
   1729 
   1730     final class LocalService implements JobSchedulerInternal {
   1731 
   1732         /**
   1733          * Returns a list of all pending jobs. A running job is not considered pending. Periodic
   1734          * jobs are always considered pending.
   1735          */
   1736         @Override
   1737         public List<JobInfo> getSystemScheduledPendingJobs() {
   1738             synchronized (mLock) {
   1739                 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
   1740                 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
   1741                     @Override
   1742                     public void process(JobStatus job) {
   1743                         if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
   1744                             pendingJobs.add(job.getJob());
   1745                         }
   1746                     }
   1747                 });
   1748                 return pendingJobs;
   1749             }
   1750         }
   1751 
   1752         @Override
   1753         public void addBackingUpUid(int uid) {
   1754             synchronized (mLock) {
   1755                 // No need to actually do anything here, since for a full backup the
   1756                 // activity manager will kill the process which will kill the job (and
   1757                 // cause it to restart, but now it can't run).
   1758                 mBackingUpUids.put(uid, uid);
   1759             }
   1760         }
   1761 
   1762         @Override
   1763         public void removeBackingUpUid(int uid) {
   1764             synchronized (mLock) {
   1765                 mBackingUpUids.delete(uid);
   1766                 // If there are any jobs for this uid, we need to rebuild the pending list
   1767                 // in case they are now ready to run.
   1768                 if (mJobs.countJobsForUid(uid) > 0) {
   1769                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
   1770                 }
   1771             }
   1772         }
   1773 
   1774         @Override
   1775         public void clearAllBackingUpUids() {
   1776             synchronized (mLock) {
   1777                 if (mBackingUpUids.size() > 0) {
   1778                     mBackingUpUids.clear();
   1779                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
   1780                 }
   1781             }
   1782         }
   1783     }
   1784 
   1785     /**
   1786      * Binder stub trampoline implementation
   1787      */
   1788     final class JobSchedulerStub extends IJobScheduler.Stub {
   1789         /** Cache determination of whether a given app can persist jobs
   1790          * key is uid of the calling app; value is undetermined/true/false
   1791          */
   1792         private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
   1793 
   1794         // Enforce that only the app itself (or shared uid participant) can schedule a
   1795         // job that runs one of the app's services, as well as verifying that the
   1796         // named service properly requires the BIND_JOB_SERVICE permission
   1797         private void enforceValidJobRequest(int uid, JobInfo job) {
   1798             final IPackageManager pm = AppGlobals.getPackageManager();
   1799             final ComponentName service = job.getService();
   1800             try {
   1801                 ServiceInfo si = pm.getServiceInfo(service,
   1802                         PackageManager.MATCH_DIRECT_BOOT_AWARE
   1803                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
   1804                         UserHandle.getUserId(uid));
   1805                 if (si == null) {
   1806                     throw new IllegalArgumentException("No such service " + service);
   1807                 }
   1808                 if (si.applicationInfo.uid != uid) {
   1809                     throw new IllegalArgumentException("uid " + uid +
   1810                             " cannot schedule job in " + service.getPackageName());
   1811                 }
   1812                 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
   1813                     throw new IllegalArgumentException("Scheduled service " + service
   1814                             + " does not require android.permission.BIND_JOB_SERVICE permission");
   1815                 }
   1816             } catch (RemoteException e) {
   1817                 // Can't happen; the Package Manager is in this same process
   1818             }
   1819         }
   1820 
   1821         private boolean canPersistJobs(int pid, int uid) {
   1822             // If we get this far we're good to go; all we need to do now is check
   1823             // whether the app is allowed to persist its scheduled work.
   1824             final boolean canPersist;
   1825             synchronized (mPersistCache) {
   1826                 Boolean cached = mPersistCache.get(uid);
   1827                 if (cached != null) {
   1828                     canPersist = cached.booleanValue();
   1829                 } else {
   1830                     // Persisting jobs is tantamount to running at boot, so we permit
   1831                     // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
   1832                     // permission
   1833                     int result = getContext().checkPermission(
   1834                             android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
   1835                     canPersist = (result == PackageManager.PERMISSION_GRANTED);
   1836                     mPersistCache.put(uid, canPersist);
   1837                 }
   1838             }
   1839             return canPersist;
   1840         }
   1841 
   1842         // IJobScheduler implementation
   1843         @Override
   1844         public int schedule(JobInfo job) throws RemoteException {
   1845             if (DEBUG) {
   1846                 Slog.d(TAG, "Scheduling job: " + job.toString());
   1847             }
   1848             final int pid = Binder.getCallingPid();
   1849             final int uid = Binder.getCallingUid();
   1850 
   1851             enforceValidJobRequest(uid, job);
   1852             if (job.isPersisted()) {
   1853                 if (!canPersistJobs(pid, uid)) {
   1854                     throw new IllegalArgumentException("Error: requested job be persisted without"
   1855                             + " holding RECEIVE_BOOT_COMPLETED permission.");
   1856                 }
   1857             }
   1858 
   1859             if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
   1860                 getContext().enforceCallingOrSelfPermission(
   1861                         android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
   1862             }
   1863 
   1864             long ident = Binder.clearCallingIdentity();
   1865             try {
   1866                 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, -1, null);
   1867             } finally {
   1868                 Binder.restoreCallingIdentity(ident);
   1869             }
   1870         }
   1871 
   1872         // IJobScheduler implementation
   1873         @Override
   1874         public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
   1875             if (DEBUG) {
   1876                 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
   1877             }
   1878             final int pid = Binder.getCallingPid();
   1879             final int uid = Binder.getCallingUid();
   1880 
   1881             enforceValidJobRequest(uid, job);
   1882             if (job.isPersisted()) {
   1883                 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
   1884             }
   1885             if (work == null) {
   1886                 throw new NullPointerException("work is null");
   1887             }
   1888 
   1889             if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
   1890                 getContext().enforceCallingOrSelfPermission(
   1891                         android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
   1892             }
   1893 
   1894             long ident = Binder.clearCallingIdentity();
   1895             try {
   1896                 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, -1, null);
   1897             } finally {
   1898                 Binder.restoreCallingIdentity(ident);
   1899             }
   1900         }
   1901 
   1902         @Override
   1903         public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
   1904                 throws RemoteException {
   1905             final int callerUid = Binder.getCallingUid();
   1906             if (DEBUG) {
   1907                 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
   1908                         + " on behalf of " + packageName);
   1909             }
   1910 
   1911             if (packageName == null) {
   1912                 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
   1913             }
   1914 
   1915             int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
   1916                     android.Manifest.permission.UPDATE_DEVICE_STATS);
   1917             if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
   1918                 throw new SecurityException("Caller uid " + callerUid
   1919                         + " not permitted to schedule jobs for other apps");
   1920             }
   1921 
   1922             if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
   1923                 getContext().enforceCallingOrSelfPermission(
   1924                         android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
   1925             }
   1926 
   1927             long ident = Binder.clearCallingIdentity();
   1928             try {
   1929                 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
   1930                         packageName, userId, tag);
   1931             } finally {
   1932                 Binder.restoreCallingIdentity(ident);
   1933             }
   1934         }
   1935 
   1936         @Override
   1937         public List<JobInfo> getAllPendingJobs() throws RemoteException {
   1938             final int uid = Binder.getCallingUid();
   1939 
   1940             long ident = Binder.clearCallingIdentity();
   1941             try {
   1942                 return JobSchedulerService.this.getPendingJobs(uid);
   1943             } finally {
   1944                 Binder.restoreCallingIdentity(ident);
   1945             }
   1946         }
   1947 
   1948         @Override
   1949         public JobInfo getPendingJob(int jobId) throws RemoteException {
   1950             final int uid = Binder.getCallingUid();
   1951 
   1952             long ident = Binder.clearCallingIdentity();
   1953             try {
   1954                 return JobSchedulerService.this.getPendingJob(uid, jobId);
   1955             } finally {
   1956                 Binder.restoreCallingIdentity(ident);
   1957             }
   1958         }
   1959 
   1960         @Override
   1961         public void cancelAll() throws RemoteException {
   1962             final int uid = Binder.getCallingUid();
   1963 
   1964             long ident = Binder.clearCallingIdentity();
   1965             try {
   1966                 JobSchedulerService.this.cancelJobsForUid(uid, "cancelAll() called by app");
   1967             } finally {
   1968                 Binder.restoreCallingIdentity(ident);
   1969             }
   1970         }
   1971 
   1972         @Override
   1973         public void cancel(int jobId) throws RemoteException {
   1974             final int uid = Binder.getCallingUid();
   1975 
   1976             long ident = Binder.clearCallingIdentity();
   1977             try {
   1978                 JobSchedulerService.this.cancelJob(uid, jobId);
   1979             } finally {
   1980                 Binder.restoreCallingIdentity(ident);
   1981             }
   1982         }
   1983 
   1984         /**
   1985          * "dumpsys" infrastructure
   1986          */
   1987         @Override
   1988         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1989             if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
   1990 
   1991             long identityToken = Binder.clearCallingIdentity();
   1992             try {
   1993                 JobSchedulerService.this.dumpInternal(pw, args);
   1994             } finally {
   1995                 Binder.restoreCallingIdentity(identityToken);
   1996             }
   1997         }
   1998 
   1999         @Override
   2000         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
   2001                 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
   2002                 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
   2003                         this, in, out, err, args, callback, resultReceiver);
   2004         }
   2005     };
   2006 
   2007     // Shell command infrastructure: run the given job immediately
   2008     int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
   2009         if (DEBUG) {
   2010             Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
   2011                     + " " + jobId + " f=" + force);
   2012         }
   2013 
   2014         try {
   2015             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
   2016                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
   2017             if (uid < 0) {
   2018                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
   2019             }
   2020 
   2021             synchronized (mLock) {
   2022                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
   2023                 if (js == null) {
   2024                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
   2025                 }
   2026 
   2027                 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
   2028                 if (!js.isConstraintsSatisfied()) {
   2029                     js.overrideState = 0;
   2030                     return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
   2031                 }
   2032 
   2033                 queueReadyJobsForExecutionLocked();
   2034                 maybeRunPendingJobsLocked();
   2035             }
   2036         } catch (RemoteException e) {
   2037             // can't happen
   2038         }
   2039         return 0;
   2040     }
   2041 
   2042     // Shell command infrastructure: immediately timeout currently executing jobs
   2043     int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
   2044             boolean hasJobId, int jobId) {
   2045         if (DEBUG) {
   2046             Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
   2047         }
   2048 
   2049         synchronized (mLock) {
   2050             boolean foundSome = false;
   2051             for (int i=0; i<mActiveServices.size(); i++) {
   2052                 mActiveServices.get(i).timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId);
   2053             }
   2054             if (!foundSome) {
   2055                 pw.println("No matching executing jobs found.");
   2056             }
   2057         }
   2058         return 0;
   2059     }
   2060 
   2061     void setMonitorBattery(boolean enabled) {
   2062         synchronized (mLock) {
   2063             if (mBatteryController != null) {
   2064                 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
   2065             }
   2066         }
   2067     }
   2068 
   2069     int getBatterySeq() {
   2070         synchronized (mLock) {
   2071             return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
   2072         }
   2073     }
   2074 
   2075     boolean getBatteryCharging() {
   2076         synchronized (mLock) {
   2077             return mBatteryController != null
   2078                     ? mBatteryController.getTracker().isOnStablePower() : false;
   2079         }
   2080     }
   2081 
   2082     boolean getBatteryNotLow() {
   2083         synchronized (mLock) {
   2084             return mBatteryController != null
   2085                     ? mBatteryController.getTracker().isBatteryNotLow() : false;
   2086         }
   2087     }
   2088 
   2089     int getStorageSeq() {
   2090         synchronized (mLock) {
   2091             return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
   2092         }
   2093     }
   2094 
   2095     boolean getStorageNotLow() {
   2096         synchronized (mLock) {
   2097             return mStorageController != null
   2098                     ? mStorageController.getTracker().isStorageNotLow() : false;
   2099         }
   2100     }
   2101 
   2102     int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
   2103         try {
   2104             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
   2105                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
   2106             if (uid < 0) {
   2107                 pw.print("unknown("); pw.print(pkgName); pw.println(")");
   2108                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
   2109             }
   2110 
   2111             synchronized (mLock) {
   2112                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
   2113                 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
   2114                 if (js == null) {
   2115                     pw.print("unknown("); UserHandle.formatUid(pw, uid);
   2116                     pw.print("/jid"); pw.print(jobId); pw.println(")");
   2117                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
   2118                 }
   2119 
   2120                 boolean printed = false;
   2121                 if (mPendingJobs.contains(js)) {
   2122                     pw.print("pending");
   2123                     printed = true;
   2124                 }
   2125                 if (isCurrentlyActiveLocked(js)) {
   2126                     if (printed) {
   2127                         pw.print(" ");
   2128                     }
   2129                     printed = true;
   2130                     pw.println("active");
   2131                 }
   2132                 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
   2133                     if (printed) {
   2134                         pw.print(" ");
   2135                     }
   2136                     printed = true;
   2137                     pw.println("user-stopped");
   2138                 }
   2139                 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
   2140                     if (printed) {
   2141                         pw.print(" ");
   2142                     }
   2143                     printed = true;
   2144                     pw.println("backing-up");
   2145                 }
   2146                 boolean componentPresent = false;
   2147                 try {
   2148                     componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
   2149                             js.getServiceComponent(),
   2150                             PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
   2151                             js.getUserId()) != null);
   2152                 } catch (RemoteException e) {
   2153                 }
   2154                 if (!componentPresent) {
   2155                     if (printed) {
   2156                         pw.print(" ");
   2157                     }
   2158                     printed = true;
   2159                     pw.println("no-component");
   2160                 }
   2161                 if (js.isReady()) {
   2162                     if (printed) {
   2163                         pw.print(" ");
   2164                     }
   2165                     printed = true;
   2166                     pw.println("ready");
   2167                 }
   2168                 if (!printed) {
   2169                     pw.print("waiting");
   2170                 }
   2171                 pw.println();
   2172             }
   2173         } catch (RemoteException e) {
   2174             // can't happen
   2175         }
   2176         return 0;
   2177     }
   2178 
   2179     private String printContextIdToJobMap(JobStatus[] map, String initial) {
   2180         StringBuilder s = new StringBuilder(initial + ": ");
   2181         for (int i=0; i<map.length; i++) {
   2182             s.append("(")
   2183                     .append(map[i] == null? -1: map[i].getJobId())
   2184                     .append(map[i] == null? -1: map[i].getUid())
   2185                     .append(")" );
   2186         }
   2187         return s.toString();
   2188     }
   2189 
   2190     private String printPendingQueue() {
   2191         StringBuilder s = new StringBuilder("Pending queue: ");
   2192         Iterator<JobStatus> it = mPendingJobs.iterator();
   2193         while (it.hasNext()) {
   2194             JobStatus js = it.next();
   2195             s.append("(")
   2196                     .append(js.getJob().getId())
   2197                     .append(", ")
   2198                     .append(js.getUid())
   2199                     .append(") ");
   2200         }
   2201         return s.toString();
   2202     }
   2203 
   2204     static void dumpHelp(PrintWriter pw) {
   2205         pw.println("Job Scheduler (jobscheduler) dump options:");
   2206         pw.println("  [-h] [package] ...");
   2207         pw.println("    -h: print this help");
   2208         pw.println("  [package] is an optional package name to limit the output to.");
   2209     }
   2210 
   2211     void dumpInternal(final PrintWriter pw, String[] args) {
   2212         int filterUid = -1;
   2213         if (!ArrayUtils.isEmpty(args)) {
   2214             int opti = 0;
   2215             while (opti < args.length) {
   2216                 String arg = args[opti];
   2217                 if ("-h".equals(arg)) {
   2218                     dumpHelp(pw);
   2219                     return;
   2220                 } else if ("-a".equals(arg)) {
   2221                     // Ignore, we always dump all.
   2222                 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
   2223                     pw.println("Unknown option: " + arg);
   2224                     return;
   2225                 } else {
   2226                     break;
   2227                 }
   2228                 opti++;
   2229             }
   2230             if (opti < args.length) {
   2231                 String pkg = args[opti];
   2232                 try {
   2233                     filterUid = getContext().getPackageManager().getPackageUid(pkg,
   2234                             PackageManager.MATCH_ANY_USER);
   2235                 } catch (NameNotFoundException ignored) {
   2236                     pw.println("Invalid package: " + pkg);
   2237                     return;
   2238                 }
   2239             }
   2240         }
   2241 
   2242         final int filterUidFinal = UserHandle.getAppId(filterUid);
   2243         final long nowElapsed = SystemClock.elapsedRealtime();
   2244         final long nowUptime = SystemClock.uptimeMillis();
   2245         synchronized (mLock) {
   2246             mConstants.dump(pw);
   2247             pw.println();
   2248             pw.println("Started users: " + Arrays.toString(mStartedUsers));
   2249             pw.print("Registered ");
   2250             pw.print(mJobs.size());
   2251             pw.println(" jobs:");
   2252             if (mJobs.size() > 0) {
   2253                 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
   2254                 Collections.sort(jobs, new Comparator<JobStatus>() {
   2255                     @Override
   2256                     public int compare(JobStatus o1, JobStatus o2) {
   2257                         int uid1 = o1.getUid();
   2258                         int uid2 = o2.getUid();
   2259                         int id1 = o1.getJobId();
   2260                         int id2 = o2.getJobId();
   2261                         if (uid1 != uid2) {
   2262                             return uid1 < uid2 ? -1 : 1;
   2263                         }
   2264                         return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
   2265                     }
   2266                 });
   2267                 for (JobStatus job : jobs) {
   2268                     pw.print("  JOB #"); job.printUniqueId(pw); pw.print(": ");
   2269                     pw.println(job.toShortStringExceptUniqueId());
   2270 
   2271                     // Skip printing details if the caller requested a filter
   2272                     if (!job.shouldDump(filterUidFinal)) {
   2273                         continue;
   2274                     }
   2275 
   2276                     job.dump(pw, "    ", true, nowElapsed);
   2277                     pw.print("    Ready: ");
   2278                     pw.print(isReadyToBeExecutedLocked(job));
   2279                     pw.print(" (job=");
   2280                     pw.print(job.isReady());
   2281                     pw.print(" user=");
   2282                     pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
   2283                     pw.print(" !pending=");
   2284                     pw.print(!mPendingJobs.contains(job));
   2285                     pw.print(" !active=");
   2286                     pw.print(!isCurrentlyActiveLocked(job));
   2287                     pw.print(" !backingup=");
   2288                     pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
   2289                     pw.print(" comp=");
   2290                     boolean componentPresent = false;
   2291                     try {
   2292                         componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
   2293                                 job.getServiceComponent(),
   2294                                 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
   2295                                 job.getUserId()) != null);
   2296                     } catch (RemoteException e) {
   2297                     }
   2298                     pw.print(componentPresent);
   2299                     pw.println(")");
   2300                 }
   2301             } else {
   2302                 pw.println("  None.");
   2303             }
   2304             for (int i=0; i<mControllers.size(); i++) {
   2305                 pw.println();
   2306                 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
   2307             }
   2308             pw.println();
   2309             pw.println("Uid priority overrides:");
   2310             for (int i=0; i< mUidPriorityOverride.size(); i++) {
   2311                 int uid = mUidPriorityOverride.keyAt(i);
   2312                 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
   2313                     pw.print("  "); pw.print(UserHandle.formatUid(uid));
   2314                     pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
   2315                 }
   2316             }
   2317             if (mBackingUpUids.size() > 0) {
   2318                 pw.println();
   2319                 pw.println("Backing up uids:");
   2320                 boolean first = true;
   2321                 for (int i = 0; i < mBackingUpUids.size(); i++) {
   2322                     int uid = mBackingUpUids.keyAt(i);
   2323                     if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
   2324                         if (first) {
   2325                             pw.print("  ");
   2326                             first = false;
   2327                         } else {
   2328                             pw.print(", ");
   2329                         }
   2330                         pw.print(UserHandle.formatUid(uid));
   2331                     }
   2332                 }
   2333                 pw.println();
   2334             }
   2335             pw.println();
   2336             mJobPackageTracker.dump(pw, "", filterUidFinal);
   2337             pw.println();
   2338             if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
   2339                 pw.println();
   2340             }
   2341             pw.println("Pending queue:");
   2342             for (int i=0; i<mPendingJobs.size(); i++) {
   2343                 JobStatus job = mPendingJobs.get(i);
   2344                 pw.print("  Pending #"); pw.print(i); pw.print(": ");
   2345                 pw.println(job.toShortString());
   2346                 job.dump(pw, "    ", false, nowElapsed);
   2347                 int priority = evaluateJobPriorityLocked(job);
   2348                 if (priority != JobInfo.PRIORITY_DEFAULT) {
   2349                     pw.print("    Evaluated priority: "); pw.println(priority);
   2350                 }
   2351                 pw.print("    Tag: "); pw.println(job.getTag());
   2352                 pw.print("    Enq: ");
   2353                 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
   2354                 pw.println();
   2355             }
   2356             pw.println();
   2357             pw.println("Active jobs:");
   2358             for (int i=0; i<mActiveServices.size(); i++) {
   2359                 JobServiceContext jsc = mActiveServices.get(i);
   2360                 pw.print("  Slot #"); pw.print(i); pw.print(": ");
   2361                 final JobStatus job = jsc.getRunningJobLocked();
   2362                 if (job == null) {
   2363                     if (jsc.mStoppedReason != null) {
   2364                         pw.print("inactive since ");
   2365                         TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
   2366                         pw.print(", stopped because: ");
   2367                         pw.println(jsc.mStoppedReason);
   2368                     } else {
   2369                         pw.println("inactive");
   2370                     }
   2371                     continue;
   2372                 } else {
   2373                     pw.println(job.toShortString());
   2374                     pw.print("    Running for: ");
   2375                     TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
   2376                     pw.print(", timeout at: ");
   2377                     TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
   2378                     pw.println();
   2379                     job.dump(pw, "    ", false, nowElapsed);
   2380                     int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
   2381                     if (priority != JobInfo.PRIORITY_DEFAULT) {
   2382                         pw.print("    Evaluated priority: "); pw.println(priority);
   2383                     }
   2384                     pw.print("    Active at ");
   2385                     TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
   2386                     pw.print(", pending for ");
   2387                     TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
   2388                     pw.println();
   2389                 }
   2390             }
   2391             if (filterUid == -1) {
   2392                 pw.println();
   2393                 pw.print("mReadyToRock="); pw.println(mReadyToRock);
   2394                 pw.print("mReportedActive="); pw.println(mReportedActive);
   2395                 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
   2396             }
   2397         }
   2398         pw.println();
   2399     }
   2400 }
   2401