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.ActivityManager;
     32 import android.app.ActivityManagerNative;
     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.content.BroadcastReceiver;
     41 import android.content.ComponentName;
     42 import android.content.ContentResolver;
     43 import android.content.Context;
     44 import android.content.Intent;
     45 import android.content.IntentFilter;
     46 import android.content.pm.IPackageManager;
     47 import android.content.pm.PackageManager;
     48 import android.content.pm.ServiceInfo;
     49 import android.content.pm.PackageManager.NameNotFoundException;
     50 import android.database.ContentObserver;
     51 import android.net.Uri;
     52 import android.os.BatteryStats;
     53 import android.os.Binder;
     54 import android.os.Handler;
     55 import android.os.Looper;
     56 import android.os.Message;
     57 import android.os.Process;
     58 import android.os.PowerManager;
     59 import android.os.RemoteException;
     60 import android.os.ResultReceiver;
     61 import android.os.ServiceManager;
     62 import android.os.SystemClock;
     63 import android.os.UserHandle;
     64 import android.provider.Settings;
     65 import android.util.KeyValueListParser;
     66 import android.util.Slog;
     67 import android.util.SparseArray;
     68 import android.util.SparseIntArray;
     69 import android.util.TimeUtils;
     70 
     71 import com.android.internal.app.IBatteryStats;
     72 import com.android.internal.app.procstats.ProcessStats;
     73 import com.android.internal.util.ArrayUtils;
     74 import com.android.server.DeviceIdleController;
     75 import com.android.server.LocalServices;
     76 import com.android.server.job.JobStore.JobStatusFunctor;
     77 import com.android.server.job.controllers.AppIdleController;
     78 import com.android.server.job.controllers.BatteryController;
     79 import com.android.server.job.controllers.ConnectivityController;
     80 import com.android.server.job.controllers.ContentObserverController;
     81 import com.android.server.job.controllers.DeviceIdleJobsController;
     82 import com.android.server.job.controllers.IdleController;
     83 import com.android.server.job.controllers.JobStatus;
     84 import com.android.server.job.controllers.StateController;
     85 import com.android.server.job.controllers.TimeController;
     86 
     87 import libcore.util.EmptyArray;
     88 
     89 /**
     90  * Responsible for taking jobs representing work to be performed by a client app, and determining
     91  * based on the criteria specified when that job should be run against the client application's
     92  * endpoint.
     93  * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
     94  * about constraints, or the state of active jobs. It receives callbacks from the various
     95  * controllers and completed jobs and operates accordingly.
     96  *
     97  * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
     98  * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
     99  * @hide
    100  */
    101 public final class JobSchedulerService extends com.android.server.SystemService
    102         implements StateChangedListener, JobCompletedListener {
    103     static final String TAG = "JobSchedulerService";
    104     public static final boolean DEBUG = false;
    105 
    106     /** The maximum number of concurrent jobs we run at one time. */
    107     private static final int MAX_JOB_CONTEXTS_COUNT = 16;
    108     /** Enforce a per-app limit on scheduled jobs? */
    109     private static final boolean ENFORCE_MAX_JOBS = true;
    110     /** The maximum number of jobs that we allow an unprivileged app to schedule */
    111     private static final int MAX_JOBS_PER_APP = 100;
    112 
    113 
    114     /** Global local for all job scheduler state. */
    115     final Object mLock = new Object();
    116     /** Master list of jobs. */
    117     final JobStore mJobs;
    118     /** Tracking amount of time each package runs for. */
    119     final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
    120 
    121     static final int MSG_JOB_EXPIRED = 0;
    122     static final int MSG_CHECK_JOB = 1;
    123     static final int MSG_STOP_JOB = 2;
    124     static final int MSG_CHECK_JOB_GREEDY = 3;
    125 
    126     /**
    127      * Track Services that have currently active or pending jobs. The index is provided by
    128      * {@link JobStatus#getServiceToken()}
    129      */
    130     final List<JobServiceContext> mActiveServices = new ArrayList<>();
    131     /** List of controllers that will notify this service of updates to jobs. */
    132     List<StateController> mControllers;
    133     /**
    134      * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
    135      * when ready to execute them.
    136      */
    137     final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
    138 
    139     int[] mStartedUsers = EmptyArray.INT;
    140 
    141     final JobHandler mHandler;
    142     final JobSchedulerStub mJobSchedulerStub;
    143 
    144     IBatteryStats mBatteryStats;
    145     PowerManager mPowerManager;
    146     DeviceIdleController.LocalService mLocalDeviceIdleController;
    147 
    148     /**
    149      * Set to true once we are allowed to run third party apps.
    150      */
    151     boolean mReadyToRock;
    152 
    153     /**
    154      * What we last reported to DeviceIdleController about whether we are active.
    155      */
    156     boolean mReportedActive;
    157 
    158     /**
    159      * Current limit on the number of concurrent JobServiceContext entries we want to
    160      * keep actively running a job.
    161      */
    162     int mMaxActiveJobs = 1;
    163 
    164     /**
    165      * Which uids are currently in the foreground.
    166      */
    167     final SparseIntArray mUidPriorityOverride = new SparseIntArray();
    168 
    169     // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
    170 
    171     /**
    172      * This array essentially stores the state of mActiveServices array.
    173      * The ith index stores the job present on the ith JobServiceContext.
    174      * We manipulate this array until we arrive at what jobs should be running on
    175      * what JobServiceContext.
    176      */
    177     JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
    178     /**
    179      * Indicates whether we need to act on this jobContext id
    180      */
    181     boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
    182     /**
    183      * The uid whose jobs we would like to assign to a context.
    184      */
    185     int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
    186 
    187     /**
    188      * All times are in milliseconds. These constants are kept synchronized with the system
    189      * global Settings. Any access to this class or its fields should be done while
    190      * holding the JobSchedulerService.mLock lock.
    191      */
    192     private final class Constants extends ContentObserver {
    193         // Key names stored in the settings value.
    194         private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
    195         private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
    196         private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
    197         private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
    198         private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
    199         private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
    200         private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
    201         private static final String KEY_FG_JOB_COUNT = "fg_job_count";
    202         private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
    203         private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
    204         private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
    205         private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
    206 
    207         private static final int DEFAULT_MIN_IDLE_COUNT = 1;
    208         private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
    209         private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
    210         private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
    211         private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
    212         private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
    213         private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
    214         private static final int DEFAULT_FG_JOB_COUNT = 4;
    215         private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
    216         private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
    217         private static final int DEFAULT_BG_LOW_JOB_COUNT = 2;
    218         private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
    219 
    220         /**
    221          * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
    222          * early.
    223          */
    224         int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
    225         /**
    226          * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
    227          * things early.
    228          */
    229         int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
    230         /**
    231          * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
    232          * things early.  1 == Run connectivity jobs as soon as ready.
    233          */
    234         int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
    235         /**
    236          * Minimum # of content trigger jobs that must be ready in order to force the JMS to
    237          * schedule things early.
    238          */
    239         int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
    240         /**
    241          * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
    242          * running some work early.  This (and thus the other min counts) is now set to 1, to
    243          * prevent any batching at this level.  Since we now do batching through doze, that is
    244          * a much better mechanism.
    245          */
    246         int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
    247         /**
    248          * This is the job execution factor that is considered to be heavy use of the system.
    249          */
    250         float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
    251         /**
    252          * This is the job execution factor that is considered to be moderate use of the system.
    253          */
    254         float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
    255         /**
    256          * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
    257          */
    258         int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
    259         /**
    260          * The maximum number of background jobs we allow when the system is in a normal
    261          * memory state.
    262          */
    263         int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
    264         /**
    265          * The maximum number of background jobs we allow when the system is in a moderate
    266          * memory state.
    267          */
    268         int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
    269         /**
    270          * The maximum number of background jobs we allow when the system is in a low
    271          * memory state.
    272          */
    273         int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
    274         /**
    275          * The maximum number of background jobs we allow when the system is in a critical
    276          * memory state.
    277          */
    278         int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
    279 
    280         private ContentResolver mResolver;
    281         private final KeyValueListParser mParser = new KeyValueListParser(',');
    282 
    283         public Constants(Handler handler) {
    284             super(handler);
    285         }
    286 
    287         public void start(ContentResolver resolver) {
    288             mResolver = resolver;
    289             mResolver.registerContentObserver(Settings.Global.getUriFor(
    290                     Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
    291             updateConstants();
    292         }
    293 
    294         @Override
    295         public void onChange(boolean selfChange, Uri uri) {
    296             updateConstants();
    297         }
    298 
    299         private void updateConstants() {
    300             synchronized (mLock) {
    301                 try {
    302                     mParser.setString(Settings.Global.getString(mResolver,
    303                             Settings.Global.ALARM_MANAGER_CONSTANTS));
    304                 } catch (IllegalArgumentException e) {
    305                     // Failed to parse the settings string, log this and move on
    306                     // with defaults.
    307                     Slog.e(TAG, "Bad device idle settings", e);
    308                 }
    309 
    310                 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
    311                         DEFAULT_MIN_IDLE_COUNT);
    312                 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
    313                         DEFAULT_MIN_CHARGING_COUNT);
    314                 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
    315                         DEFAULT_MIN_CONNECTIVITY_COUNT);
    316                 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
    317                         DEFAULT_MIN_CONTENT_COUNT);
    318                 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
    319                         DEFAULT_MIN_READY_JOBS_COUNT);
    320                 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
    321                         DEFAULT_HEAVY_USE_FACTOR);
    322                 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
    323                         DEFAULT_MODERATE_USE_FACTOR);
    324                 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
    325                         DEFAULT_FG_JOB_COUNT);
    326                 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
    327                         DEFAULT_BG_NORMAL_JOB_COUNT);
    328                 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
    329                     BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
    330                 }
    331                 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
    332                         DEFAULT_BG_MODERATE_JOB_COUNT);
    333                 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
    334                     BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
    335                 }
    336                 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
    337                         DEFAULT_BG_LOW_JOB_COUNT);
    338                 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
    339                     BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
    340                 }
    341                 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
    342                         DEFAULT_BG_CRITICAL_JOB_COUNT);
    343                 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
    344                     BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
    345                 }
    346             }
    347         }
    348 
    349         void dump(PrintWriter pw) {
    350             pw.println("  Settings:");
    351 
    352             pw.print("    "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("=");
    353             pw.print(MIN_IDLE_COUNT); pw.println();
    354 
    355             pw.print("    "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("=");
    356             pw.print(MIN_CHARGING_COUNT); pw.println();
    357 
    358             pw.print("    "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("=");
    359             pw.print(MIN_CONNECTIVITY_COUNT); pw.println();
    360 
    361             pw.print("    "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("=");
    362             pw.print(MIN_CONTENT_COUNT); pw.println();
    363 
    364             pw.print("    "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("=");
    365             pw.print(MIN_READY_JOBS_COUNT); pw.println();
    366 
    367             pw.print("    "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("=");
    368             pw.print(HEAVY_USE_FACTOR); pw.println();
    369 
    370             pw.print("    "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("=");
    371             pw.print(MODERATE_USE_FACTOR); pw.println();
    372 
    373             pw.print("    "); pw.print(KEY_FG_JOB_COUNT); pw.print("=");
    374             pw.print(FG_JOB_COUNT); pw.println();
    375 
    376             pw.print("    "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("=");
    377             pw.print(BG_NORMAL_JOB_COUNT); pw.println();
    378 
    379             pw.print("    "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("=");
    380             pw.print(BG_MODERATE_JOB_COUNT); pw.println();
    381 
    382             pw.print("    "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("=");
    383             pw.print(BG_LOW_JOB_COUNT); pw.println();
    384 
    385             pw.print("    "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("=");
    386             pw.print(BG_CRITICAL_JOB_COUNT); pw.println();
    387         }
    388     }
    389 
    390     final Constants mConstants;
    391 
    392     /**
    393      * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
    394      * still clean up. On reinstall the package will have a new uid.
    395      */
    396     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
    397         @Override
    398         public void onReceive(Context context, Intent intent) {
    399             if (DEBUG) {
    400                 Slog.d(TAG, "Receieved: " + intent.getAction());
    401             }
    402             if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
    403                 // Purge the app's jobs if the whole package was just disabled.  When this is
    404                 // the case the component name will be a bare package name.
    405                 final String pkgName = getPackageName(intent);
    406                 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
    407                 if (pkgName != null && pkgUid != -1) {
    408                     final String[] changedComponents = intent.getStringArrayExtra(
    409                             Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
    410                     if (changedComponents != null) {
    411                         for (String component : changedComponents) {
    412                             if (component.equals(pkgName)) {
    413                                 if (DEBUG) {
    414                                     Slog.d(TAG, "Package state change: " + pkgName);
    415                                 }
    416                                 try {
    417                                     final int userId = UserHandle.getUserId(pkgUid);
    418                                     IPackageManager pm = AppGlobals.getPackageManager();
    419                                     final int state = pm.getApplicationEnabledSetting(pkgName, userId);
    420                                     if (state == COMPONENT_ENABLED_STATE_DISABLED
    421                                             || state ==  COMPONENT_ENABLED_STATE_DISABLED_USER) {
    422                                         if (DEBUG) {
    423                                             Slog.d(TAG, "Removing jobs for package " + pkgName
    424                                                     + " in user " + userId);
    425                                         }
    426                                         cancelJobsForUid(pkgUid, true);
    427                                     }
    428                                 } catch (RemoteException e) { /* cannot happen */ }
    429                                 break;
    430                             }
    431                         }
    432                     }
    433                 } else {
    434                     Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
    435                 }
    436             } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
    437                 // If this is an outright uninstall rather than the first half of an
    438                 // app update sequence, cancel the jobs associated with the app.
    439                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
    440                     int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
    441                     if (DEBUG) {
    442                         Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
    443                     }
    444                     cancelJobsForUid(uidRemoved, true);
    445                 }
    446             } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
    447                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
    448                 if (DEBUG) {
    449                     Slog.d(TAG, "Removing jobs for user: " + userId);
    450                 }
    451                 cancelJobsForUser(userId);
    452             }
    453         }
    454     };
    455 
    456     private String getPackageName(Intent intent) {
    457         Uri uri = intent.getData();
    458         String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
    459         return pkg;
    460     }
    461 
    462     final private IUidObserver mUidObserver = new IUidObserver.Stub() {
    463         @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
    464             updateUidState(uid, procState);
    465         }
    466 
    467         @Override public void onUidGone(int uid) throws RemoteException {
    468             updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
    469         }
    470 
    471         @Override public void onUidActive(int uid) throws RemoteException {
    472         }
    473 
    474         @Override public void onUidIdle(int uid) throws RemoteException {
    475             cancelJobsForUid(uid, false);
    476         }
    477     };
    478 
    479     public Object getLock() {
    480         return mLock;
    481     }
    482 
    483     public JobStore getJobStore() {
    484         return mJobs;
    485     }
    486 
    487     @Override
    488     public void onStartUser(int userHandle) {
    489         mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
    490         // Let's kick any outstanding jobs for this user.
    491         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    492     }
    493 
    494     @Override
    495     public void onUnlockUser(int userHandle) {
    496         // Let's kick any outstanding jobs for this user.
    497         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    498     }
    499 
    500     @Override
    501     public void onStopUser(int userHandle) {
    502         mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
    503     }
    504 
    505     /**
    506      * Entry point from client to schedule the provided job.
    507      * This cancels the job if it's already been scheduled, and replaces it with the one provided.
    508      * @param job JobInfo object containing execution parameters
    509      * @param uId The package identifier of the application this job is for.
    510      * @return Result of this operation. See <code>JobScheduler#RESULT_*</code> return codes.
    511      */
    512     public int schedule(JobInfo job, int uId) {
    513         return scheduleAsPackage(job, uId, null, -1, null);
    514     }
    515 
    516     public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId,
    517             String tag) {
    518         JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
    519         try {
    520             if (ActivityManagerNative.getDefault().getAppStartMode(uId,
    521                     job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) {
    522                 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
    523                         + " -- package not allowed to start");
    524                 return JobScheduler.RESULT_FAILURE;
    525             }
    526         } catch (RemoteException e) {
    527         }
    528         if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
    529         JobStatus toCancel;
    530         synchronized (mLock) {
    531             // Jobs on behalf of others don't apply to the per-app job cap
    532             if (ENFORCE_MAX_JOBS && packageName == null) {
    533                 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
    534                     Slog.w(TAG, "Too many jobs for uid " + uId);
    535                     throw new IllegalStateException("Apps may not schedule more than "
    536                                 + MAX_JOBS_PER_APP + " distinct jobs");
    537                 }
    538             }
    539 
    540             toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
    541             if (toCancel != null) {
    542                 cancelJobImpl(toCancel, jobStatus);
    543             }
    544             startTrackingJob(jobStatus, toCancel);
    545         }
    546         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    547         return JobScheduler.RESULT_SUCCESS;
    548     }
    549 
    550     public List<JobInfo> getPendingJobs(int uid) {
    551         synchronized (mLock) {
    552             List<JobStatus> jobs = mJobs.getJobsByUid(uid);
    553             ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
    554             for (int i = jobs.size() - 1; i >= 0; i--) {
    555                 JobStatus job = jobs.get(i);
    556                 outList.add(job.getJob());
    557             }
    558             return outList;
    559         }
    560     }
    561 
    562     public JobInfo getPendingJob(int uid, int jobId) {
    563         synchronized (mLock) {
    564             List<JobStatus> jobs = mJobs.getJobsByUid(uid);
    565             for (int i = jobs.size() - 1; i >= 0; i--) {
    566                 JobStatus job = jobs.get(i);
    567                 if (job.getJobId() == jobId) {
    568                     return job.getJob();
    569                 }
    570             }
    571             return null;
    572         }
    573     }
    574 
    575     void cancelJobsForUser(int userHandle) {
    576         List<JobStatus> jobsForUser;
    577         synchronized (mLock) {
    578             jobsForUser = mJobs.getJobsByUser(userHandle);
    579         }
    580         for (int i=0; i<jobsForUser.size(); i++) {
    581             JobStatus toRemove = jobsForUser.get(i);
    582             cancelJobImpl(toRemove, null);
    583         }
    584     }
    585 
    586     /**
    587      * Entry point from client to cancel all jobs originating from their uid.
    588      * This will remove the job from the master list, and cancel the job if it was staged for
    589      * execution or being executed.
    590      * @param uid Uid to check against for removal of a job.
    591      * @param forceAll If true, all jobs for the uid will be canceled; if false, only those
    592      * whose apps are stopped.
    593      */
    594     public void cancelJobsForUid(int uid, boolean forceAll) {
    595         List<JobStatus> jobsForUid;
    596         synchronized (mLock) {
    597             jobsForUid = mJobs.getJobsByUid(uid);
    598         }
    599         for (int i=0; i<jobsForUid.size(); i++) {
    600             JobStatus toRemove = jobsForUid.get(i);
    601             if (!forceAll) {
    602                 String packageName = toRemove.getServiceComponent().getPackageName();
    603                 try {
    604                     if (ActivityManagerNative.getDefault().getAppStartMode(uid, packageName)
    605                             != ActivityManager.APP_START_MODE_DISABLED) {
    606                         continue;
    607                     }
    608                 } catch (RemoteException e) {
    609                 }
    610             }
    611             cancelJobImpl(toRemove, null);
    612         }
    613     }
    614 
    615     /**
    616      * Entry point from client to cancel the job corresponding to the jobId provided.
    617      * This will remove the job from the master list, and cancel the job if it was staged for
    618      * execution or being executed.
    619      * @param uid Uid of the calling client.
    620      * @param jobId Id of the job, provided at schedule-time.
    621      */
    622     public void cancelJob(int uid, int jobId) {
    623         JobStatus toCancel;
    624         synchronized (mLock) {
    625             toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
    626         }
    627         if (toCancel != null) {
    628             cancelJobImpl(toCancel, null);
    629         }
    630     }
    631 
    632     private void cancelJobImpl(JobStatus cancelled, JobStatus incomingJob) {
    633         if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
    634         stopTrackingJob(cancelled, incomingJob, true /* writeBack */);
    635         synchronized (mLock) {
    636             // Remove from pending queue.
    637             if (mPendingJobs.remove(cancelled)) {
    638                 mJobPackageTracker.noteNonpending(cancelled);
    639             }
    640             // Cancel if running.
    641             stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
    642             reportActive();
    643         }
    644     }
    645 
    646     void updateUidState(int uid, int procState) {
    647         synchronized (mLock) {
    648             if (procState == ActivityManager.PROCESS_STATE_TOP) {
    649                 // Only use this if we are exactly the top app.  All others can live
    650                 // with just the foreground priority.  This means that persistent processes
    651                 // can never be the top app priority...  that is fine.
    652                 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
    653             } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
    654                 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
    655             } else {
    656                 mUidPriorityOverride.delete(uid);
    657             }
    658         }
    659     }
    660 
    661     @Override
    662     public void onDeviceIdleStateChanged(boolean deviceIdle) {
    663         synchronized (mLock) {
    664             if (deviceIdle) {
    665                 // When becoming idle, make sure no jobs are actively running,
    666                 // except those using the idle exemption flag.
    667                 for (int i=0; i<mActiveServices.size(); i++) {
    668                     JobServiceContext jsc = mActiveServices.get(i);
    669                     final JobStatus executing = jsc.getRunningJob();
    670                     if (executing != null
    671                             && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
    672                         jsc.cancelExecutingJob(JobParameters.REASON_DEVICE_IDLE);
    673                     }
    674                 }
    675             } else {
    676                 // When coming out of idle, allow thing to start back up.
    677                 if (mReadyToRock) {
    678                     if (mLocalDeviceIdleController != null) {
    679                         if (!mReportedActive) {
    680                             mReportedActive = true;
    681                             mLocalDeviceIdleController.setJobsActive(true);
    682                         }
    683                     }
    684                 }
    685                 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    686             }
    687         }
    688     }
    689 
    690     void reportActive() {
    691         // active is true if pending queue contains jobs OR some job is running.
    692         boolean active = mPendingJobs.size() > 0;
    693         if (mPendingJobs.size() <= 0) {
    694             for (int i=0; i<mActiveServices.size(); i++) {
    695                 final JobServiceContext jsc = mActiveServices.get(i);
    696                 final JobStatus job = jsc.getRunningJob();
    697                 if (job != null
    698                         && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
    699                         && !job.dozeWhitelisted) {
    700                     // We will report active if we have a job running and it is not an exception
    701                     // due to being in the foreground or whitelisted.
    702                     active = true;
    703                     break;
    704                 }
    705             }
    706         }
    707 
    708         if (mReportedActive != active) {
    709             mReportedActive = active;
    710             if (mLocalDeviceIdleController != null) {
    711                 mLocalDeviceIdleController.setJobsActive(active);
    712             }
    713         }
    714     }
    715 
    716     /**
    717      * Initializes the system service.
    718      * <p>
    719      * Subclasses must define a single argument constructor that accepts the context
    720      * and passes it to super.
    721      * </p>
    722      *
    723      * @param context The system server context.
    724      */
    725     public JobSchedulerService(Context context) {
    726         super(context);
    727         mHandler = new JobHandler(context.getMainLooper());
    728         mConstants = new Constants(mHandler);
    729         mJobSchedulerStub = new JobSchedulerStub();
    730         mJobs = JobStore.initAndGet(this);
    731 
    732         // Create the controllers.
    733         mControllers = new ArrayList<StateController>();
    734         mControllers.add(ConnectivityController.get(this));
    735         mControllers.add(TimeController.get(this));
    736         mControllers.add(IdleController.get(this));
    737         mControllers.add(BatteryController.get(this));
    738         mControllers.add(AppIdleController.get(this));
    739         mControllers.add(ContentObserverController.get(this));
    740         mControllers.add(DeviceIdleJobsController.get(this));
    741     }
    742 
    743     @Override
    744     public void onStart() {
    745         publishLocalService(JobSchedulerInternal.class, new LocalService());
    746         publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
    747     }
    748 
    749     @Override
    750     public void onBootPhase(int phase) {
    751         if (PHASE_SYSTEM_SERVICES_READY == phase) {
    752             mConstants.start(getContext().getContentResolver());
    753             // Register br for package removals and user removals.
    754             final IntentFilter filter = new IntentFilter();
    755             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    756             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    757             filter.addDataScheme("package");
    758             getContext().registerReceiverAsUser(
    759                     mBroadcastReceiver, UserHandle.ALL, filter, null, null);
    760             final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
    761             getContext().registerReceiverAsUser(
    762                     mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
    763             mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
    764             try {
    765                 ActivityManagerNative.getDefault().registerUidObserver(mUidObserver,
    766                         ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
    767                         | ActivityManager.UID_OBSERVER_IDLE);
    768             } catch (RemoteException e) {
    769                 // ignored; both services live in system_server
    770             }
    771         } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
    772             synchronized (mLock) {
    773                 // Let's go!
    774                 mReadyToRock = true;
    775                 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
    776                         BatteryStats.SERVICE_NAME));
    777                 mLocalDeviceIdleController
    778                         = LocalServices.getService(DeviceIdleController.LocalService.class);
    779                 // Create the "runners".
    780                 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
    781                     mActiveServices.add(
    782                             new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
    783                                     getContext().getMainLooper()));
    784                 }
    785                 // Attach jobs to their controllers.
    786                 mJobs.forEachJob(new JobStatusFunctor() {
    787                     @Override
    788                     public void process(JobStatus job) {
    789                         for (int controller = 0; controller < mControllers.size(); controller++) {
    790                             final StateController sc = mControllers.get(controller);
    791                             sc.maybeStartTrackingJobLocked(job, null);
    792                         }
    793                     }
    794                 });
    795                 // GO GO GO!
    796                 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    797             }
    798         }
    799     }
    800 
    801     /**
    802      * Called when we have a job status object that we need to insert in our
    803      * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
    804      * about.
    805      */
    806     private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
    807         synchronized (mLock) {
    808             final boolean update = mJobs.add(jobStatus);
    809             if (mReadyToRock) {
    810                 for (int i = 0; i < mControllers.size(); i++) {
    811                     StateController controller = mControllers.get(i);
    812                     if (update) {
    813                         controller.maybeStopTrackingJobLocked(jobStatus, null, true);
    814                     }
    815                     controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
    816                 }
    817             }
    818         }
    819     }
    820 
    821     /**
    822      * Called when we want to remove a JobStatus object that we've finished executing. Returns the
    823      * object removed.
    824      */
    825     private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,
    826             boolean writeBack) {
    827         synchronized (mLock) {
    828             // Remove from store as well as controllers.
    829             final boolean removed = mJobs.remove(jobStatus, writeBack);
    830             if (removed && mReadyToRock) {
    831                 for (int i=0; i<mControllers.size(); i++) {
    832                     StateController controller = mControllers.get(i);
    833                     controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
    834                 }
    835             }
    836             return removed;
    837         }
    838     }
    839 
    840     private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) {
    841         for (int i=0; i<mActiveServices.size(); i++) {
    842             JobServiceContext jsc = mActiveServices.get(i);
    843             final JobStatus executing = jsc.getRunningJob();
    844             if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
    845                 jsc.cancelExecutingJob(reason);
    846                 return true;
    847             }
    848         }
    849         return false;
    850     }
    851 
    852     /**
    853      * @param job JobStatus we are querying against.
    854      * @return Whether or not the job represented by the status object is currently being run or
    855      * is pending.
    856      */
    857     private boolean isCurrentlyActiveLocked(JobStatus job) {
    858         for (int i=0; i<mActiveServices.size(); i++) {
    859             JobServiceContext serviceContext = mActiveServices.get(i);
    860             final JobStatus running = serviceContext.getRunningJob();
    861             if (running != null && running.matches(job.getUid(), job.getJobId())) {
    862                 return true;
    863             }
    864         }
    865         return false;
    866     }
    867 
    868     void noteJobsPending(List<JobStatus> jobs) {
    869         for (int i = jobs.size() - 1; i >= 0; i--) {
    870             JobStatus job = jobs.get(i);
    871             mJobPackageTracker.notePending(job);
    872         }
    873     }
    874 
    875     void noteJobsNonpending(List<JobStatus> jobs) {
    876         for (int i = jobs.size() - 1; i >= 0; i--) {
    877             JobStatus job = jobs.get(i);
    878             mJobPackageTracker.noteNonpending(job);
    879         }
    880     }
    881 
    882     /**
    883      * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
    884      * specify an override deadline on a failed job (the failed job will run even though it's not
    885      * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
    886      * ready job with {@link JobStatus#numFailures} > 0 will be executed.
    887      *
    888      * @param failureToReschedule Provided job status that we will reschedule.
    889      * @return A newly instantiated JobStatus with the same constraints as the last job except
    890      * with adjusted timing constraints.
    891      *
    892      * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH
    893      */
    894     private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {
    895         final long elapsedNowMillis = SystemClock.elapsedRealtime();
    896         final JobInfo job = failureToReschedule.getJob();
    897 
    898         final long initialBackoffMillis = job.getInitialBackoffMillis();
    899         final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
    900         long delayMillis;
    901 
    902         switch (job.getBackoffPolicy()) {
    903             case JobInfo.BACKOFF_POLICY_LINEAR:
    904                 delayMillis = initialBackoffMillis * backoffAttempts;
    905                 break;
    906             default:
    907                 if (DEBUG) {
    908                     Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
    909                 }
    910             case JobInfo.BACKOFF_POLICY_EXPONENTIAL:
    911                 delayMillis =
    912                         (long) Math.scalb(initialBackoffMillis, backoffAttempts - 1);
    913                 break;
    914         }
    915         delayMillis =
    916                 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
    917         JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
    918                 JobStatus.NO_LATEST_RUNTIME, backoffAttempts);
    919         for (int ic=0; ic<mControllers.size(); ic++) {
    920             StateController controller = mControllers.get(ic);
    921             controller.rescheduleForFailure(newJob, failureToReschedule);
    922         }
    923         return newJob;
    924     }
    925 
    926     /**
    927      * Called after a periodic has executed so we can reschedule it. We take the last execution
    928      * time of the job to be the time of completion (i.e. the time at which this function is
    929      * called).
    930      * This could be inaccurate b/c the job can run for as long as
    931      * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
    932      * to underscheduling at least, rather than if we had taken the last execution time to be the
    933      * start of the execution.
    934      * @return A new job representing the execution criteria for this instantiation of the
    935      * recurring job.
    936      */
    937     private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
    938         final long elapsedNow = SystemClock.elapsedRealtime();
    939         // Compute how much of the period is remaining.
    940         long runEarly = 0L;
    941 
    942         // If this periodic was rescheduled it won't have a deadline.
    943         if (periodicToReschedule.hasDeadlineConstraint()) {
    944             runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
    945         }
    946         long flex = periodicToReschedule.getJob().getFlexMillis();
    947         long period = periodicToReschedule.getJob().getIntervalMillis();
    948         long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
    949         long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
    950 
    951         if (DEBUG) {
    952             Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
    953                     newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
    954         }
    955         return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
    956                 newLatestRuntimeElapsed, 0 /* backoffAttempt */);
    957     }
    958 
    959     // JobCompletedListener implementations.
    960 
    961     /**
    962      * A job just finished executing. We fetch the
    963      * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
    964      * whether we want to reschedule we readd it to the controllers.
    965      * @param jobStatus Completed job.
    966      * @param needsReschedule Whether the implementing class should reschedule this job.
    967      */
    968     @Override
    969     public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
    970         if (DEBUG) {
    971             Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
    972         }
    973         // Do not write back immediately if this is a periodic job. The job may get lost if system
    974         // shuts down before it is added back.
    975         if (!stopTrackingJob(jobStatus, null, !jobStatus.getJob().isPeriodic())) {
    976             if (DEBUG) {
    977                 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
    978             }
    979             // We still want to check for jobs to execute, because this job may have
    980             // scheduled a new job under the same job id, and now we can run it.
    981             mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
    982             return;
    983         }
    984         // Note: there is a small window of time in here where, when rescheduling a job,
    985         // we will stop monitoring its content providers.  This should be fixed by stopping
    986         // the old job after scheduling the new one, but since we have no lock held here
    987         // that may cause ordering problems if the app removes jobStatus while in here.
    988         if (needsReschedule) {
    989             JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
    990             startTrackingJob(rescheduled, jobStatus);
    991         } else if (jobStatus.getJob().isPeriodic()) {
    992             JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
    993             startTrackingJob(rescheduledPeriodic, jobStatus);
    994         }
    995         reportActive();
    996         mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
    997     }
    998 
    999     // StateChangedListener implementations.
   1000 
   1001     /**
   1002      * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
   1003      * some controller's state has changed, so as to run through the list of jobs and start/stop
   1004      * any that are eligible.
   1005      */
   1006     @Override
   1007     public void onControllerStateChanged() {
   1008         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
   1009     }
   1010 
   1011     @Override
   1012     public void onRunJobNow(JobStatus jobStatus) {
   1013         mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
   1014     }
   1015 
   1016     private class JobHandler extends Handler {
   1017 
   1018         public JobHandler(Looper looper) {
   1019             super(looper);
   1020         }
   1021 
   1022         @Override
   1023         public void handleMessage(Message message) {
   1024             synchronized (mLock) {
   1025                 if (!mReadyToRock) {
   1026                     return;
   1027                 }
   1028             }
   1029             switch (message.what) {
   1030                 case MSG_JOB_EXPIRED:
   1031                     synchronized (mLock) {
   1032                         JobStatus runNow = (JobStatus) message.obj;
   1033                         // runNow can be null, which is a controller's way of indicating that its
   1034                         // state is such that all ready jobs should be run immediately.
   1035                         if (runNow != null && !mPendingJobs.contains(runNow)
   1036                                 && mJobs.containsJob(runNow)) {
   1037                             mJobPackageTracker.notePending(runNow);
   1038                             mPendingJobs.add(runNow);
   1039                         }
   1040                         queueReadyJobsForExecutionLockedH();
   1041                     }
   1042                     break;
   1043                 case MSG_CHECK_JOB:
   1044                     synchronized (mLock) {
   1045                         if (mReportedActive) {
   1046                             // if jobs are currently being run, queue all ready jobs for execution.
   1047                             queueReadyJobsForExecutionLockedH();
   1048                         } else {
   1049                             // Check the list of jobs and run some of them if we feel inclined.
   1050                             maybeQueueReadyJobsForExecutionLockedH();
   1051                         }
   1052                     }
   1053                     break;
   1054                 case MSG_CHECK_JOB_GREEDY:
   1055                     synchronized (mLock) {
   1056                         queueReadyJobsForExecutionLockedH();
   1057                     }
   1058                     break;
   1059                 case MSG_STOP_JOB:
   1060                     cancelJobImpl((JobStatus)message.obj, null);
   1061                     break;
   1062             }
   1063             maybeRunPendingJobsH();
   1064             // Don't remove JOB_EXPIRED in case one came along while processing the queue.
   1065             removeMessages(MSG_CHECK_JOB);
   1066         }
   1067 
   1068         /**
   1069          * Run through list of jobs and execute all possible - at least one is expired so we do
   1070          * as many as we can.
   1071          */
   1072         private void queueReadyJobsForExecutionLockedH() {
   1073             if (DEBUG) {
   1074                 Slog.d(TAG, "queuing all ready jobs for execution:");
   1075             }
   1076             noteJobsNonpending(mPendingJobs);
   1077             mPendingJobs.clear();
   1078             mJobs.forEachJob(mReadyQueueFunctor);
   1079             mReadyQueueFunctor.postProcess();
   1080 
   1081             if (DEBUG) {
   1082                 final int queuedJobs = mPendingJobs.size();
   1083                 if (queuedJobs == 0) {
   1084                     Slog.d(TAG, "No jobs pending.");
   1085                 } else {
   1086                     Slog.d(TAG, queuedJobs + " jobs queued.");
   1087                 }
   1088             }
   1089         }
   1090 
   1091         class ReadyJobQueueFunctor implements JobStatusFunctor {
   1092             ArrayList<JobStatus> newReadyJobs;
   1093 
   1094             @Override
   1095             public void process(JobStatus job) {
   1096                 if (isReadyToBeExecutedLocked(job)) {
   1097                     if (DEBUG) {
   1098                         Slog.d(TAG, "    queued " + job.toShortString());
   1099                     }
   1100                     if (newReadyJobs == null) {
   1101                         newReadyJobs = new ArrayList<JobStatus>();
   1102                     }
   1103                     newReadyJobs.add(job);
   1104                 } else if (areJobConstraintsNotSatisfiedLocked(job)) {
   1105                     stopJobOnServiceContextLocked(job,
   1106                             JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
   1107                 }
   1108             }
   1109 
   1110             public void postProcess() {
   1111                 if (newReadyJobs != null) {
   1112                     noteJobsPending(newReadyJobs);
   1113                     mPendingJobs.addAll(newReadyJobs);
   1114                 }
   1115                 newReadyJobs = null;
   1116             }
   1117         }
   1118         private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
   1119 
   1120         /**
   1121          * The state of at least one job has changed. Here is where we could enforce various
   1122          * policies on when we want to execute jobs.
   1123          * Right now the policy is such:
   1124          * If >1 of the ready jobs is idle mode we send all of them off
   1125          * if more than 2 network connectivity jobs are ready we send them all off.
   1126          * If more than 4 jobs total are ready we send them all off.
   1127          * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
   1128          */
   1129         class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
   1130             int chargingCount;
   1131             int idleCount;
   1132             int backoffCount;
   1133             int connectivityCount;
   1134             int contentCount;
   1135             List<JobStatus> runnableJobs;
   1136 
   1137             public MaybeReadyJobQueueFunctor() {
   1138                 reset();
   1139             }
   1140 
   1141             // Functor method invoked for each job via JobStore.forEachJob()
   1142             @Override
   1143             public void process(JobStatus job) {
   1144                 if (isReadyToBeExecutedLocked(job)) {
   1145                     try {
   1146                         if (ActivityManagerNative.getDefault().getAppStartMode(job.getUid(),
   1147                                 job.getJob().getService().getPackageName())
   1148                                 == ActivityManager.APP_START_MODE_DISABLED) {
   1149                             Slog.w(TAG, "Aborting job " + job.getUid() + ":"
   1150                                     + job.getJob().toString() + " -- package not allowed to start");
   1151                             mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
   1152                             return;
   1153                         }
   1154                     } catch (RemoteException e) {
   1155                     }
   1156                     if (job.getNumFailures() > 0) {
   1157                         backoffCount++;
   1158                     }
   1159                     if (job.hasIdleConstraint()) {
   1160                         idleCount++;
   1161                     }
   1162                     if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()
   1163                             || job.hasNotRoamingConstraint()) {
   1164                         connectivityCount++;
   1165                     }
   1166                     if (job.hasChargingConstraint()) {
   1167                         chargingCount++;
   1168                     }
   1169                     if (job.hasContentTriggerConstraint()) {
   1170                         contentCount++;
   1171                     }
   1172                     if (runnableJobs == null) {
   1173                         runnableJobs = new ArrayList<>();
   1174                     }
   1175                     runnableJobs.add(job);
   1176                 } else if (areJobConstraintsNotSatisfiedLocked(job)) {
   1177                     stopJobOnServiceContextLocked(job,
   1178                             JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
   1179                 }
   1180             }
   1181 
   1182             public void postProcess() {
   1183                 if (backoffCount > 0 ||
   1184                         idleCount >= mConstants.MIN_IDLE_COUNT ||
   1185                         connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
   1186                         chargingCount >= mConstants.MIN_CHARGING_COUNT ||
   1187                         contentCount >= mConstants.MIN_CONTENT_COUNT ||
   1188                         (runnableJobs != null
   1189                                 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
   1190                     if (DEBUG) {
   1191                         Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
   1192                     }
   1193                     noteJobsPending(runnableJobs);
   1194                     mPendingJobs.addAll(runnableJobs);
   1195                 } else {
   1196                     if (DEBUG) {
   1197                         Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
   1198                     }
   1199                 }
   1200 
   1201                 // Be ready for next time
   1202                 reset();
   1203             }
   1204 
   1205             private void reset() {
   1206                 chargingCount = 0;
   1207                 idleCount =  0;
   1208                 backoffCount = 0;
   1209                 connectivityCount = 0;
   1210                 contentCount = 0;
   1211                 runnableJobs = null;
   1212             }
   1213         }
   1214         private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
   1215 
   1216         private void maybeQueueReadyJobsForExecutionLockedH() {
   1217             if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
   1218 
   1219             noteJobsNonpending(mPendingJobs);
   1220             mPendingJobs.clear();
   1221             mJobs.forEachJob(mMaybeQueueFunctor);
   1222             mMaybeQueueFunctor.postProcess();
   1223         }
   1224 
   1225         /**
   1226          * Criteria for moving a job into the pending queue:
   1227          *      - It's ready.
   1228          *      - It's not pending.
   1229          *      - It's not already running on a JSC.
   1230          *      - The user that requested the job is running.
   1231          *      - The component is enabled and runnable.
   1232          */
   1233         private boolean isReadyToBeExecutedLocked(JobStatus job) {
   1234             final boolean jobReady = job.isReady();
   1235             final boolean jobPending = mPendingJobs.contains(job);
   1236             final boolean jobActive = isCurrentlyActiveLocked(job);
   1237 
   1238             final int userId = job.getUserId();
   1239             final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
   1240             final boolean componentPresent;
   1241             try {
   1242                 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
   1243                         job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
   1244                         userId) != null);
   1245             } catch (RemoteException e) {
   1246                 throw e.rethrowAsRuntimeException();
   1247             }
   1248 
   1249             if (DEBUG) {
   1250                 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
   1251                         + " ready=" + jobReady + " pending=" + jobPending
   1252                         + " active=" + jobActive + " userStarted=" + userStarted
   1253                         + " componentPresent=" + componentPresent);
   1254             }
   1255             return userStarted && componentPresent && jobReady && !jobPending && !jobActive;
   1256         }
   1257 
   1258         /**
   1259          * Criteria for cancelling an active job:
   1260          *      - It's not ready
   1261          *      - It's running on a JSC.
   1262          */
   1263         private boolean areJobConstraintsNotSatisfiedLocked(JobStatus job) {
   1264             return !job.isReady() && isCurrentlyActiveLocked(job);
   1265         }
   1266 
   1267         /**
   1268          * Reconcile jobs in the pending queue against available execution contexts.
   1269          * A controller can force a job into the pending queue even if it's already running, but
   1270          * here is where we decide whether to actually execute it.
   1271          */
   1272         private void maybeRunPendingJobsH() {
   1273             synchronized (mLock) {
   1274                 if (DEBUG) {
   1275                     Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
   1276                 }
   1277                 assignJobsToContextsLocked();
   1278                 reportActive();
   1279             }
   1280         }
   1281     }
   1282 
   1283     private int adjustJobPriority(int curPriority, JobStatus job) {
   1284         if (curPriority < JobInfo.PRIORITY_TOP_APP) {
   1285             float factor = mJobPackageTracker.getLoadFactor(job);
   1286             if (factor >= mConstants.HEAVY_USE_FACTOR) {
   1287                 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
   1288             } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
   1289                 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
   1290             }
   1291         }
   1292         return curPriority;
   1293     }
   1294 
   1295     private int evaluateJobPriorityLocked(JobStatus job) {
   1296         int priority = job.getPriority();
   1297         if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
   1298             return adjustJobPriority(priority, job);
   1299         }
   1300         int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
   1301         if (override != 0) {
   1302             return adjustJobPriority(override, job);
   1303         }
   1304         return adjustJobPriority(priority, job);
   1305     }
   1306 
   1307     /**
   1308      * Takes jobs from pending queue and runs them on available contexts.
   1309      * If no contexts are available, preempts lower priority jobs to
   1310      * run higher priority ones.
   1311      * Lock on mJobs before calling this function.
   1312      */
   1313     private void assignJobsToContextsLocked() {
   1314         if (DEBUG) {
   1315             Slog.d(TAG, printPendingQueue());
   1316         }
   1317 
   1318         int memLevel;
   1319         try {
   1320             memLevel = ActivityManagerNative.getDefault().getMemoryTrimLevel();
   1321         } catch (RemoteException e) {
   1322             memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
   1323         }
   1324         switch (memLevel) {
   1325             case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
   1326                 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
   1327                 break;
   1328             case ProcessStats.ADJ_MEM_FACTOR_LOW:
   1329                 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
   1330                 break;
   1331             case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
   1332                 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
   1333                 break;
   1334             default:
   1335                 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
   1336                 break;
   1337         }
   1338 
   1339         JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
   1340         boolean[] act = mTmpAssignAct;
   1341         int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
   1342         int numActive = 0;
   1343         int numForeground = 0;
   1344         for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
   1345             final JobServiceContext js = mActiveServices.get(i);
   1346             final JobStatus status = js.getRunningJob();
   1347             if ((contextIdToJobMap[i] = status) != null) {
   1348                 numActive++;
   1349                 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
   1350                     numForeground++;
   1351                 }
   1352             }
   1353             act[i] = false;
   1354             preferredUidForContext[i] = js.getPreferredUid();
   1355         }
   1356         if (DEBUG) {
   1357             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
   1358         }
   1359         for (int i=0; i<mPendingJobs.size(); i++) {
   1360             JobStatus nextPending = mPendingJobs.get(i);
   1361 
   1362             // If job is already running, go to next job.
   1363             int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
   1364             if (jobRunningContext != -1) {
   1365                 continue;
   1366             }
   1367 
   1368             final int priority = evaluateJobPriorityLocked(nextPending);
   1369             nextPending.lastEvaluatedPriority = priority;
   1370 
   1371             // Find a context for nextPending. The context should be available OR
   1372             // it should have lowest priority among all running jobs
   1373             // (sharing the same Uid as nextPending)
   1374             int minPriority = Integer.MAX_VALUE;
   1375             int minPriorityContextId = -1;
   1376             for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
   1377                 JobStatus job = contextIdToJobMap[j];
   1378                 int preferredUid = preferredUidForContext[j];
   1379                 if (job == null) {
   1380                     if ((numActive < mMaxActiveJobs ||
   1381                             (priority >= JobInfo.PRIORITY_TOP_APP &&
   1382                                     numForeground < mConstants.FG_JOB_COUNT)) &&
   1383                             (preferredUid == nextPending.getUid() ||
   1384                                     preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
   1385                         // This slot is free, and we haven't yet hit the limit on
   1386                         // concurrent jobs...  we can just throw the job in to here.
   1387                         minPriorityContextId = j;
   1388                         break;
   1389                     }
   1390                     // No job on this context, but nextPending can't run here because
   1391                     // the context has a preferred Uid or we have reached the limit on
   1392                     // concurrent jobs.
   1393                     continue;
   1394                 }
   1395                 if (job.getUid() != nextPending.getUid()) {
   1396                     continue;
   1397                 }
   1398                 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
   1399                     continue;
   1400                 }
   1401                 if (minPriority > nextPending.lastEvaluatedPriority) {
   1402                     minPriority = nextPending.lastEvaluatedPriority;
   1403                     minPriorityContextId = j;
   1404                 }
   1405             }
   1406             if (minPriorityContextId != -1) {
   1407                 contextIdToJobMap[minPriorityContextId] = nextPending;
   1408                 act[minPriorityContextId] = true;
   1409                 numActive++;
   1410                 if (priority >= JobInfo.PRIORITY_TOP_APP) {
   1411                     numForeground++;
   1412                 }
   1413             }
   1414         }
   1415         if (DEBUG) {
   1416             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
   1417         }
   1418         mJobPackageTracker.noteConcurrency(numActive, numForeground);
   1419         for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
   1420             boolean preservePreferredUid = false;
   1421             if (act[i]) {
   1422                 JobStatus js = mActiveServices.get(i).getRunningJob();
   1423                 if (js != null) {
   1424                     if (DEBUG) {
   1425                         Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob());
   1426                     }
   1427                     // preferredUid will be set to uid of currently running job.
   1428                     mActiveServices.get(i).preemptExecutingJob();
   1429                     preservePreferredUid = true;
   1430                 } else {
   1431                     final JobStatus pendingJob = contextIdToJobMap[i];
   1432                     if (DEBUG) {
   1433                         Slog.d(TAG, "About to run job on context "
   1434                                 + String.valueOf(i) + ", job: " + pendingJob);
   1435                     }
   1436                     for (int ic=0; ic<mControllers.size(); ic++) {
   1437                         mControllers.get(ic).prepareForExecutionLocked(pendingJob);
   1438                     }
   1439                     if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
   1440                         Slog.d(TAG, "Error executing " + pendingJob);
   1441                     }
   1442                     if (mPendingJobs.remove(pendingJob)) {
   1443                         mJobPackageTracker.noteNonpending(pendingJob);
   1444                     }
   1445                 }
   1446             }
   1447             if (!preservePreferredUid) {
   1448                 mActiveServices.get(i).clearPreferredUid();
   1449             }
   1450         }
   1451     }
   1452 
   1453     int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
   1454         for (int i=0; i<map.length; i++) {
   1455             if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
   1456                 return i;
   1457             }
   1458         }
   1459         return -1;
   1460     }
   1461 
   1462     final class LocalService implements JobSchedulerInternal {
   1463 
   1464         /**
   1465          * Returns a list of all pending jobs. A running job is not considered pending. Periodic
   1466          * jobs are always considered pending.
   1467          */
   1468         @Override
   1469         public List<JobInfo> getSystemScheduledPendingJobs() {
   1470             synchronized (mLock) {
   1471                 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
   1472                 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
   1473                     @Override
   1474                     public void process(JobStatus job) {
   1475                         if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
   1476                             pendingJobs.add(job.getJob());
   1477                         }
   1478                     }
   1479                 });
   1480                 return pendingJobs;
   1481             }
   1482         }
   1483     }
   1484 
   1485     /**
   1486      * Binder stub trampoline implementation
   1487      */
   1488     final class JobSchedulerStub extends IJobScheduler.Stub {
   1489         /** Cache determination of whether a given app can persist jobs
   1490          * key is uid of the calling app; value is undetermined/true/false
   1491          */
   1492         private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
   1493 
   1494         // Enforce that only the app itself (or shared uid participant) can schedule a
   1495         // job that runs one of the app's services, as well as verifying that the
   1496         // named service properly requires the BIND_JOB_SERVICE permission
   1497         private void enforceValidJobRequest(int uid, JobInfo job) {
   1498             final IPackageManager pm = AppGlobals.getPackageManager();
   1499             final ComponentName service = job.getService();
   1500             try {
   1501                 ServiceInfo si = pm.getServiceInfo(service,
   1502                         PackageManager.MATCH_DIRECT_BOOT_AWARE
   1503                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
   1504                         UserHandle.getUserId(uid));
   1505                 if (si == null) {
   1506                     throw new IllegalArgumentException("No such service " + service);
   1507                 }
   1508                 if (si.applicationInfo.uid != uid) {
   1509                     throw new IllegalArgumentException("uid " + uid +
   1510                             " cannot schedule job in " + service.getPackageName());
   1511                 }
   1512                 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
   1513                     throw new IllegalArgumentException("Scheduled service " + service
   1514                             + " does not require android.permission.BIND_JOB_SERVICE permission");
   1515                 }
   1516             } catch (RemoteException e) {
   1517                 // Can't happen; the Package Manager is in this same process
   1518             }
   1519         }
   1520 
   1521         private boolean canPersistJobs(int pid, int uid) {
   1522             // If we get this far we're good to go; all we need to do now is check
   1523             // whether the app is allowed to persist its scheduled work.
   1524             final boolean canPersist;
   1525             synchronized (mPersistCache) {
   1526                 Boolean cached = mPersistCache.get(uid);
   1527                 if (cached != null) {
   1528                     canPersist = cached.booleanValue();
   1529                 } else {
   1530                     // Persisting jobs is tantamount to running at boot, so we permit
   1531                     // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
   1532                     // permission
   1533                     int result = getContext().checkPermission(
   1534                             android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
   1535                     canPersist = (result == PackageManager.PERMISSION_GRANTED);
   1536                     mPersistCache.put(uid, canPersist);
   1537                 }
   1538             }
   1539             return canPersist;
   1540         }
   1541 
   1542         // IJobScheduler implementation
   1543         @Override
   1544         public int schedule(JobInfo job) throws RemoteException {
   1545             if (DEBUG) {
   1546                 Slog.d(TAG, "Scheduling job: " + job.toString());
   1547             }
   1548             final int pid = Binder.getCallingPid();
   1549             final int uid = Binder.getCallingUid();
   1550 
   1551             enforceValidJobRequest(uid, job);
   1552             if (job.isPersisted()) {
   1553                 if (!canPersistJobs(pid, uid)) {
   1554                     throw new IllegalArgumentException("Error: requested job be persisted without"
   1555                             + " holding RECEIVE_BOOT_COMPLETED permission.");
   1556                 }
   1557             }
   1558 
   1559             long ident = Binder.clearCallingIdentity();
   1560             try {
   1561                 return JobSchedulerService.this.schedule(job, uid);
   1562             } finally {
   1563                 Binder.restoreCallingIdentity(ident);
   1564             }
   1565         }
   1566 
   1567         @Override
   1568         public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
   1569                 throws RemoteException {
   1570             final int callerUid = Binder.getCallingUid();
   1571             if (DEBUG) {
   1572                 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
   1573                         + " on behalf of " + packageName);
   1574             }
   1575 
   1576             if (packageName == null) {
   1577                 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
   1578             }
   1579 
   1580             int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
   1581                     android.Manifest.permission.UPDATE_DEVICE_STATS);
   1582             if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
   1583                 throw new SecurityException("Caller uid " + callerUid
   1584                         + " not permitted to schedule jobs for other apps");
   1585             }
   1586 
   1587             if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
   1588                 getContext().enforceCallingOrSelfPermission(
   1589                         android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
   1590             }
   1591 
   1592             long ident = Binder.clearCallingIdentity();
   1593             try {
   1594                 return JobSchedulerService.this.scheduleAsPackage(job, callerUid,
   1595                         packageName, userId, tag);
   1596             } finally {
   1597                 Binder.restoreCallingIdentity(ident);
   1598             }
   1599         }
   1600 
   1601         @Override
   1602         public List<JobInfo> getAllPendingJobs() throws RemoteException {
   1603             final int uid = Binder.getCallingUid();
   1604 
   1605             long ident = Binder.clearCallingIdentity();
   1606             try {
   1607                 return JobSchedulerService.this.getPendingJobs(uid);
   1608             } finally {
   1609                 Binder.restoreCallingIdentity(ident);
   1610             }
   1611         }
   1612 
   1613         @Override
   1614         public JobInfo getPendingJob(int jobId) throws RemoteException {
   1615             final int uid = Binder.getCallingUid();
   1616 
   1617             long ident = Binder.clearCallingIdentity();
   1618             try {
   1619                 return JobSchedulerService.this.getPendingJob(uid, jobId);
   1620             } finally {
   1621                 Binder.restoreCallingIdentity(ident);
   1622             }
   1623         }
   1624 
   1625         @Override
   1626         public void cancelAll() throws RemoteException {
   1627             final int uid = Binder.getCallingUid();
   1628 
   1629             long ident = Binder.clearCallingIdentity();
   1630             try {
   1631                 JobSchedulerService.this.cancelJobsForUid(uid, true);
   1632             } finally {
   1633                 Binder.restoreCallingIdentity(ident);
   1634             }
   1635         }
   1636 
   1637         @Override
   1638         public void cancel(int jobId) throws RemoteException {
   1639             final int uid = Binder.getCallingUid();
   1640 
   1641             long ident = Binder.clearCallingIdentity();
   1642             try {
   1643                 JobSchedulerService.this.cancelJob(uid, jobId);
   1644             } finally {
   1645                 Binder.restoreCallingIdentity(ident);
   1646             }
   1647         }
   1648 
   1649         /**
   1650          * "dumpsys" infrastructure
   1651          */
   1652         @Override
   1653         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1654             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
   1655 
   1656             long identityToken = Binder.clearCallingIdentity();
   1657             try {
   1658                 JobSchedulerService.this.dumpInternal(pw, args);
   1659             } finally {
   1660                 Binder.restoreCallingIdentity(identityToken);
   1661             }
   1662         }
   1663 
   1664         @Override
   1665         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
   1666                 String[] args, ResultReceiver resultReceiver) throws RemoteException {
   1667                 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
   1668                         this, in, out, err, args, resultReceiver);
   1669         }
   1670     };
   1671 
   1672     // Shell command infrastructure: run the given job immediately
   1673     int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
   1674         if (DEBUG) {
   1675             Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
   1676                     + " " + jobId + " f=" + force);
   1677         }
   1678 
   1679         try {
   1680             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId);
   1681             if (uid < 0) {
   1682                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
   1683             }
   1684 
   1685             synchronized (mLock) {
   1686                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
   1687                 if (js == null) {
   1688                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
   1689                 }
   1690 
   1691                 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
   1692                 if (!js.isConstraintsSatisfied()) {
   1693                     js.overrideState = 0;
   1694                     return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
   1695                 }
   1696 
   1697                 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
   1698             }
   1699         } catch (RemoteException e) {
   1700             // can't happen
   1701         }
   1702         return 0;
   1703     }
   1704 
   1705     private String printContextIdToJobMap(JobStatus[] map, String initial) {
   1706         StringBuilder s = new StringBuilder(initial + ": ");
   1707         for (int i=0; i<map.length; i++) {
   1708             s.append("(")
   1709                     .append(map[i] == null? -1: map[i].getJobId())
   1710                     .append(map[i] == null? -1: map[i].getUid())
   1711                     .append(")" );
   1712         }
   1713         return s.toString();
   1714     }
   1715 
   1716     private String printPendingQueue() {
   1717         StringBuilder s = new StringBuilder("Pending queue: ");
   1718         Iterator<JobStatus> it = mPendingJobs.iterator();
   1719         while (it.hasNext()) {
   1720             JobStatus js = it.next();
   1721             s.append("(")
   1722                     .append(js.getJob().getId())
   1723                     .append(", ")
   1724                     .append(js.getUid())
   1725                     .append(") ");
   1726         }
   1727         return s.toString();
   1728     }
   1729 
   1730     static void dumpHelp(PrintWriter pw) {
   1731         pw.println("Job Scheduler (jobscheduler) dump options:");
   1732         pw.println("  [-h] [package] ...");
   1733         pw.println("    -h: print this help");
   1734         pw.println("  [package] is an optional package name to limit the output to.");
   1735     }
   1736 
   1737     void dumpInternal(final PrintWriter pw, String[] args) {
   1738         int filterUid = -1;
   1739         if (!ArrayUtils.isEmpty(args)) {
   1740             int opti = 0;
   1741             while (opti < args.length) {
   1742                 String arg = args[opti];
   1743                 if ("-h".equals(arg)) {
   1744                     dumpHelp(pw);
   1745                     return;
   1746                 } else if ("-a".equals(arg)) {
   1747                     // Ignore, we always dump all.
   1748                 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
   1749                     pw.println("Unknown option: " + arg);
   1750                     return;
   1751                 } else {
   1752                     break;
   1753                 }
   1754                 opti++;
   1755             }
   1756             if (opti < args.length) {
   1757                 String pkg = args[opti];
   1758                 try {
   1759                     filterUid = getContext().getPackageManager().getPackageUid(pkg,
   1760                             PackageManager.MATCH_UNINSTALLED_PACKAGES);
   1761                 } catch (NameNotFoundException ignored) {
   1762                     pw.println("Invalid package: " + pkg);
   1763                     return;
   1764                 }
   1765             }
   1766         }
   1767 
   1768         final int filterUidFinal = UserHandle.getAppId(filterUid);
   1769         final long now = SystemClock.elapsedRealtime();
   1770         synchronized (mLock) {
   1771             mConstants.dump(pw);
   1772             pw.println();
   1773             pw.println("Started users: " + Arrays.toString(mStartedUsers));
   1774             pw.print("Registered ");
   1775             pw.print(mJobs.size());
   1776             pw.println(" jobs:");
   1777             if (mJobs.size() > 0) {
   1778                 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
   1779                 Collections.sort(jobs, new Comparator<JobStatus>() {
   1780                     @Override
   1781                     public int compare(JobStatus o1, JobStatus o2) {
   1782                         int uid1 = o1.getUid();
   1783                         int uid2 = o2.getUid();
   1784                         int id1 = o1.getJobId();
   1785                         int id2 = o2.getJobId();
   1786                         if (uid1 != uid2) {
   1787                             return uid1 < uid2 ? -1 : 1;
   1788                         }
   1789                         return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
   1790                     }
   1791                 });
   1792                 for (JobStatus job : jobs) {
   1793                     pw.print("  JOB #"); job.printUniqueId(pw); pw.print(": ");
   1794                     pw.println(job.toShortStringExceptUniqueId());
   1795 
   1796                     // Skip printing details if the caller requested a filter
   1797                     if (!job.shouldDump(filterUidFinal)) {
   1798                         continue;
   1799                     }
   1800 
   1801                     job.dump(pw, "    ", true);
   1802                     pw.print("    Ready: ");
   1803                     pw.print(mHandler.isReadyToBeExecutedLocked(job));
   1804                     pw.print(" (job=");
   1805                     pw.print(job.isReady());
   1806                     pw.print(" pending=");
   1807                     pw.print(mPendingJobs.contains(job));
   1808                     pw.print(" active=");
   1809                     pw.print(isCurrentlyActiveLocked(job));
   1810                     pw.print(" user=");
   1811                     pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
   1812                     pw.println(")");
   1813                 }
   1814             } else {
   1815                 pw.println("  None.");
   1816             }
   1817             for (int i=0; i<mControllers.size(); i++) {
   1818                 pw.println();
   1819                 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
   1820             }
   1821             pw.println();
   1822             pw.println("Uid priority overrides:");
   1823             for (int i=0; i< mUidPriorityOverride.size(); i++) {
   1824                 int uid = mUidPriorityOverride.keyAt(i);
   1825                 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
   1826                     pw.print("  "); pw.print(UserHandle.formatUid(uid));
   1827                     pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
   1828                 }
   1829             }
   1830             pw.println();
   1831             mJobPackageTracker.dump(pw, "", filterUidFinal);
   1832             pw.println();
   1833             if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
   1834                 pw.println();
   1835             }
   1836             pw.println("Pending queue:");
   1837             for (int i=0; i<mPendingJobs.size(); i++) {
   1838                 JobStatus job = mPendingJobs.get(i);
   1839                 pw.print("  Pending #"); pw.print(i); pw.print(": ");
   1840                 pw.println(job.toShortString());
   1841                 job.dump(pw, "    ", false);
   1842                 int priority = evaluateJobPriorityLocked(job);
   1843                 if (priority != JobInfo.PRIORITY_DEFAULT) {
   1844                     pw.print("    Evaluated priority: "); pw.println(priority);
   1845                 }
   1846                 pw.print("    Tag: "); pw.println(job.getTag());
   1847             }
   1848             pw.println();
   1849             pw.println("Active jobs:");
   1850             for (int i=0; i<mActiveServices.size(); i++) {
   1851                 JobServiceContext jsc = mActiveServices.get(i);
   1852                 pw.print("  Slot #"); pw.print(i); pw.print(": ");
   1853                 if (jsc.getRunningJob() == null) {
   1854                     pw.println("inactive");
   1855                     continue;
   1856                 } else {
   1857                     pw.println(jsc.getRunningJob().toShortString());
   1858                     pw.print("    Running for: ");
   1859                     TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw);
   1860                     pw.print(", timeout at: ");
   1861                     TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw);
   1862                     pw.println();
   1863                     jsc.getRunningJob().dump(pw, "    ", false);
   1864                     int priority = evaluateJobPriorityLocked(jsc.getRunningJob());
   1865                     if (priority != JobInfo.PRIORITY_DEFAULT) {
   1866                         pw.print("    Evaluated priority: "); pw.println(priority);
   1867                     }
   1868                 }
   1869             }
   1870             if (filterUid == -1) {
   1871                 pw.println();
   1872                 pw.print("mReadyToRock="); pw.println(mReadyToRock);
   1873                 pw.print("mReportedActive="); pw.println(mReportedActive);
   1874                 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
   1875             }
   1876         }
   1877         pw.println();
   1878     }
   1879 }
   1880