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