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