Home | History | Annotate | Download | only in controllers
      1 /*
      2  * Copyright (C) 2016 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.controllers;
     18 
     19 import android.content.BroadcastReceiver;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.IntentFilter;
     23 import android.os.PowerManager;
     24 import android.os.UserHandle;
     25 import android.util.Slog;
     26 
     27 import com.android.internal.util.ArrayUtils;
     28 import com.android.server.DeviceIdleController;
     29 import com.android.server.LocalServices;
     30 import com.android.server.job.JobSchedulerService;
     31 import com.android.server.job.JobStore;
     32 
     33 import java.io.PrintWriter;
     34 import java.util.Arrays;
     35 
     36 /**
     37  * When device is dozing, set constraint for all jobs, except whitelisted apps, as not satisfied.
     38  * When device is not dozing, set constraint for all jobs as satisfied.
     39  */
     40 public final class DeviceIdleJobsController extends StateController {
     41 
     42     private static final String LOG_TAG = "DeviceIdleJobsController";
     43     private static final boolean LOG_DEBUG = false;
     44 
     45     // Singleton factory
     46     private static Object sCreationLock = new Object();
     47     private static DeviceIdleJobsController sController;
     48 
     49     private final JobSchedulerService mJobSchedulerService;
     50     private final PowerManager mPowerManager;
     51     private final DeviceIdleController.LocalService mLocalDeviceIdleController;
     52 
     53     /**
     54      * True when in device idle mode, so we don't want to schedule any jobs.
     55      */
     56     private boolean mDeviceIdleMode;
     57     private int[] mDeviceIdleWhitelistAppIds;
     58 
     59     final JobStore.JobStatusFunctor mUpdateFunctor = new JobStore.JobStatusFunctor() {
     60         @Override public void process(JobStatus jobStatus) {
     61             updateTaskStateLocked(jobStatus);
     62         }
     63     };
     64 
     65     /**
     66      * Returns a singleton for the DeviceIdleJobsController
     67      */
     68     public static DeviceIdleJobsController get(JobSchedulerService service) {
     69         synchronized (sCreationLock) {
     70             if (sController == null) {
     71                 sController = new DeviceIdleJobsController(service, service.getContext(),
     72                         service.getLock());
     73             }
     74             return sController;
     75         }
     76     }
     77 
     78     // onReceive
     79     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
     80         @Override
     81         public void onReceive(Context context, Intent intent) {
     82             final String action = intent.getAction();
     83             if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(action)
     84                     || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
     85                 updateIdleMode(mPowerManager != null
     86                         ? (mPowerManager.isDeviceIdleMode()
     87                                 || mPowerManager.isLightDeviceIdleMode())
     88                         : false);
     89             } else if (PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED.equals(action)) {
     90                 updateWhitelist();
     91             }
     92         }
     93     };
     94 
     95     private DeviceIdleJobsController(JobSchedulerService jobSchedulerService, Context context,
     96             Object lock) {
     97         super(jobSchedulerService, context, lock);
     98 
     99         mJobSchedulerService = jobSchedulerService;
    100         // Register for device idle mode changes
    101         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
    102         mLocalDeviceIdleController =
    103                 LocalServices.getService(DeviceIdleController.LocalService.class);
    104         final IntentFilter filter = new IntentFilter();
    105         filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
    106         filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
    107         filter.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
    108         mContext.registerReceiverAsUser(
    109                 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
    110     }
    111 
    112     void updateIdleMode(boolean enabled) {
    113         boolean changed = false;
    114         // Need the whitelist to be ready when going into idle
    115         if (mDeviceIdleWhitelistAppIds == null) {
    116             updateWhitelist();
    117         }
    118         synchronized (mLock) {
    119             if (mDeviceIdleMode != enabled) {
    120                 changed = true;
    121             }
    122             mDeviceIdleMode = enabled;
    123             if (LOG_DEBUG) Slog.d(LOG_TAG, "mDeviceIdleMode=" + mDeviceIdleMode);
    124             mJobSchedulerService.getJobStore().forEachJob(mUpdateFunctor);
    125         }
    126         // Inform the job scheduler service about idle mode changes
    127         if (changed) {
    128             mStateChangedListener.onDeviceIdleStateChanged(enabled);
    129         }
    130     }
    131 
    132     /**
    133      * Fetches the latest whitelist from the device idle controller.
    134      */
    135     void updateWhitelist() {
    136         synchronized (mLock) {
    137             if (mLocalDeviceIdleController != null) {
    138                 mDeviceIdleWhitelistAppIds =
    139                         mLocalDeviceIdleController.getPowerSaveWhitelistUserAppIds();
    140                 if (LOG_DEBUG) {
    141                     Slog.d(LOG_TAG, "Got whitelist " + Arrays.toString(mDeviceIdleWhitelistAppIds));
    142                 }
    143             }
    144         }
    145     }
    146 
    147     /**
    148      * Checks if the given job's scheduling app id exists in the device idle user whitelist.
    149      */
    150     boolean isWhitelistedLocked(JobStatus job) {
    151         if (mDeviceIdleWhitelistAppIds != null
    152                 && ArrayUtils.contains(mDeviceIdleWhitelistAppIds,
    153                         UserHandle.getAppId(job.getSourceUid()))) {
    154             return true;
    155         }
    156         return false;
    157     }
    158 
    159     private void updateTaskStateLocked(JobStatus task) {
    160         final boolean whitelisted = isWhitelistedLocked(task);
    161         final boolean enableTask = !mDeviceIdleMode || whitelisted;
    162         task.setDeviceNotDozingConstraintSatisfied(enableTask, whitelisted);
    163     }
    164 
    165     @Override
    166     public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
    167         updateTaskStateLocked(jobStatus);
    168     }
    169 
    170     @Override
    171     public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
    172             boolean forUpdate) {
    173     }
    174 
    175     @Override
    176     public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) {
    177         pw.println("DeviceIdleJobsController");
    178         mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
    179             @Override public void process(JobStatus jobStatus) {
    180                 if (!jobStatus.shouldDump(filterUid)) {
    181                     return;
    182                 }
    183                 pw.print("  #");
    184                 jobStatus.printUniqueId(pw);
    185                 pw.print(" from ");
    186                 UserHandle.formatUid(pw, jobStatus.getSourceUid());
    187                 pw.print(": ");
    188                 pw.print(jobStatus.getSourcePackageName());
    189                 pw.print((jobStatus.satisfiedConstraints
    190                         & JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0
    191                                 ? " RUNNABLE" : " WAITING");
    192                 if (jobStatus.dozeWhitelisted) {
    193                     pw.print(" WHITELISTED");
    194                 }
    195                 pw.println();
    196             }
    197         });
    198     }
    199 }