From cf1a2f73fc102be2ac7060ac97d4682bb2565ca5 Mon Sep 17 00:00:00 2001
From: Christopher Tate <ctate@google.com>
Date: Mon, 16 Jun 2014 15:51:39 -0700
Subject: Switch everything to scheduled jobs

Everything that used the IdleMaintenance APIs/broadcasts gets to use the
spiffy new JobScheduler instead.  Hooray!

On top of that, the now-obsolete "idle maintenance" APIs are now gone
entirely.  Double hooray!

Bug 14993295

Change-Id: I5fb67c296ca8cd0ba8a2c8760a0f0d9d962d813b
---
 .../com/android/server/IdleMaintenanceService.java | 818 ---------------------
 .../android/server/pm/BackgroundDexOptService.java |  68 +-
 services/java/com/android/server/SystemServer.java |   9 +-
 3 files changed, 36 insertions(+), 859 deletions(-)
 delete mode 100644 services/core/java/com/android/server/IdleMaintenanceService.java

(limited to 'services')

diff --git a/services/core/java/com/android/server/IdleMaintenanceService.java b/services/core/java/com/android/server/IdleMaintenanceService.java
deleted file mode 100644
index acc6abe..0000000
--- a/services/core/java/com/android/server/IdleMaintenanceService.java
+++ /dev/null
@@ -1,818 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.maintenance.IIdleCallback;
-import android.app.maintenance.IIdleService;
-import android.app.maintenance.IdleService;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Handler;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.WorkSource;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Random;
-
-/**
- * This service observes the device state and when applicable sends
- * broadcasts at the beginning and at the end of a period during which
- * observers can perform idle maintenance tasks. Typical use of the
- * idle maintenance is to perform somehow expensive tasks that can be
- * postponed to a moment when they will not degrade user experience.
- *
- * The current implementation is very simple. The start of a maintenance
- * window is announced if: the screen is off or showing a dream AND the
- * battery level is more than twenty percent AND at least one hour passed
- * activity).
- *
- * The end of a maintenance window is announced only if: a start was
- * announced AND the screen turned on or a dream was stopped.
- *
- * Method naming note:
- * Methods whose name ends with "Tm" must only be called from the main thread.
- */
-public class IdleMaintenanceService extends BroadcastReceiver {
-
-    private static final boolean DEBUG = false;
-
-    private static final String TAG = IdleMaintenanceService.class.getSimpleName();
-
-    private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
-
-    private static final long MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS = 24 * 60 * 60 * 1000; // 1 day
-
-    private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING = 30; // percent
-
-    private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING = 80; // percent
-
-    private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 20; // percent
-
-    private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 71 * 60 * 1000; // 71 min
-
-    private static final long MAX_IDLE_MAINTENANCE_DURATION = 71 * 60 * 1000; // 71 min
-
-    private static final String ACTION_UPDATE_IDLE_MAINTENANCE_STATE =
-        "com.android.server.IdleMaintenanceService.action.UPDATE_IDLE_MAINTENANCE_STATE";
-
-    private static final String ACTION_FORCE_IDLE_MAINTENANCE =
-        "com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE";
-
-    static final int MSG_OP_COMPLETE = 1;
-    static final int MSG_IDLE_FINISHED = 2;
-    static final int MSG_TIMEOUT = 3;
-
-    // when a timeout happened, what were we expecting?
-    static final int VERB_BINDING = 1;
-    static final int VERB_IDLING = 2;
-    static final int VERB_ENDING = 3;
-
-    // What are our relevant timeouts / allocated slices?
-    static final long OP_TIMEOUT = 8 * 1000;  // 8 seconds to bind or ack the start
-    static final long IDLE_TIMESLICE = 10 * 60 * 1000;  // ten minutes for each idler
-
-    private final AlarmManager mAlarmService;
-    private final BatteryService mBatteryService;
-    private final PendingIntent mUpdateIdleMaintenanceStatePendingIntent;
-    private final Context mContext;
-    private final WakeLock mWakeLock;
-    private final WorkSource mSystemWorkSource = new WorkSource(Process.myUid());
-
-    private long mLastIdleMaintenanceStartTimeMillis;
-    private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
-    private boolean mIdleMaintenanceStarted;
-
-    final IdleCallback mCallback;
-    final Handler mHandler;
-
-    final Random mTokenGenerator = new Random();
-
-    int makeToken() {
-        int token;
-        do  {
-            token = mTokenGenerator.nextInt(Integer.MAX_VALUE);
-        } while (token == 0);
-        return token;
-    }
-
-    class ActiveTask {
-        public IdleServiceInfo who;
-        public int verb;
-        public int token;
-
-        ActiveTask(IdleServiceInfo target, int action) {
-            who = target;
-            verb = action;
-            token = makeToken();
-        }
-
-        @Override
-        public String toString() {
-            return "ActiveTask{" + Integer.toHexString(this.hashCode())
-                    + " : verb=" + verb
-                    + " : token=" + token
-                    + " : "+ who + "}";
-        }
-    }
-
-    // What operations are in flight?
-    final SparseArray<ActiveTask> mPendingOperations = new SparseArray<ActiveTask>();
-
-    // Idle service queue management
-    class IdleServiceInfo {
-        public final ComponentName componentName;
-        public final int uid;
-        public IIdleService service;
-
-        IdleServiceInfo(ResolveInfo info, ComponentName cname) {
-            componentName = cname;  // derived from 'info' but this avoids an extra object
-            uid = info.serviceInfo.applicationInfo.uid;
-            service = null;
-        }
-
-        @Override
-        public int hashCode() {
-            return componentName.hashCode();
-        }
-
-        @Override
-        public String toString() {
-            return "IdleServiceInfo{" + componentName
-                    + " / " + (service == null ? "null" : service.asBinder()) + "}";
-        }
-    }
-
-    final ArrayMap<ComponentName, IdleServiceInfo> mIdleServices =
-            new ArrayMap<ComponentName, IdleServiceInfo>();
-    final LinkedList<IdleServiceInfo> mIdleServiceQueue = new LinkedList<IdleServiceInfo>();
-    IdleServiceInfo mCurrentIdler;  // set when we've committed to launching an idler
-    IdleServiceInfo mLastIdler;     // end of queue when idling begins
-
-    void reportNoTimeout(int token, boolean result) {
-        final Message msg = mHandler.obtainMessage(MSG_OP_COMPLETE, result ? 1 : 0, token);
-        mHandler.sendMessage(msg);
-    }
-
-    // Binder acknowledgment trampoline
-    class IdleCallback extends IIdleCallback.Stub {
-        @Override
-        public void acknowledgeStart(int token, boolean result) throws RemoteException {
-            reportNoTimeout(token, result);
-        }
-
-        @Override
-        public void acknowledgeStop(int token) throws RemoteException {
-            reportNoTimeout(token, false);
-        }
-
-        @Override
-        public void idleFinished(int token) throws RemoteException {
-            if (DEBUG) {
-                Slog.v(TAG, "idleFinished: " + token);
-            }
-            final Message msg = mHandler.obtainMessage(MSG_IDLE_FINISHED, 0, token);
-            mHandler.sendMessage(msg);
-        }
-    }
-
-    // Stuff that we run on a Handler
-    class IdleHandler extends Handler {
-        public IdleHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            final int token = msg.arg2;
-
-            switch (msg.what) {
-                case MSG_OP_COMPLETE: {
-                    if (DEBUG) {
-                        Slog.i(TAG, "MSG_OP_COMPLETE of " + token);
-                    }
-                    ActiveTask task = mPendingOperations.get(token);
-                    if (task != null) {
-                        mPendingOperations.remove(token);
-                        removeMessages(MSG_TIMEOUT);
-
-                        handleOpCompleteTm(task, msg.arg1);
-                    } else {
-                        // Can happen in a race between timeout and actual
-                        // (belated) completion of a "begin idling" or similar
-                        // operation.  In that state we've already processed the
-                        // timeout, so we intentionally no-op here.
-                        if (DEBUG) {
-                            Slog.w(TAG, "Belated op-complete of " + token);
-                        }
-                    }
-                    break;
-                }
-
-                case MSG_IDLE_FINISHED: {
-                    if (DEBUG) {
-                        Slog.i(TAG, "MSG_IDLE_FINISHED of " + token);
-                    }
-                    ActiveTask task = mPendingOperations.get(token);
-                    if (task != null) {
-                        if (DEBUG) {
-                            Slog.i(TAG, "... removing task " + token);
-                        }
-                        mPendingOperations.remove(token);
-                        removeMessages(MSG_TIMEOUT);
-
-                        handleIdleFinishedTm(task);
-                    } else {
-                        // Can happen "legitimately" from an app explicitly calling
-                        // idleFinished() after already having been told that its slice
-                        // has ended.
-                        if (DEBUG) {
-                            Slog.w(TAG, "Belated idle-finished of " + token);
-                        }
-                    }
-                    break;
-                }
-
-                case MSG_TIMEOUT: {
-                    if (DEBUG) {
-                        Slog.i(TAG, "MSG_TIMEOUT of " + token);
-                    }
-                    ActiveTask task = mPendingOperations.get(token);
-                    if (task != null) {
-                        mPendingOperations.remove(token);
-                        removeMessages(MSG_OP_COMPLETE);
-
-                        handleTimeoutTm(task);
-                    } else {
-                        // This one should not happen; we flushed timeout messages
-                        // whenever we entered a state after which we have established
-                        // that they are not appropriate.
-                        Slog.w(TAG, "Unexpected timeout of " + token);
-                    }
-                    break;
-                }
-
-                default:
-                    Slog.w(TAG, "Unknown message: " + msg.what);
-            }
-        }
-    }
-
-    void handleTimeoutTm(ActiveTask task) {
-        switch (task.verb) {
-        case VERB_BINDING: {
-            // We were trying to bind to this service, but it wedged or otherwise
-            // failed to respond in time.  Let it stay in the queue for the next
-            // time around, but just give up on it for now and go on to the next.
-            startNextIdleServiceTm();
-            break;
-        }
-        case VERB_IDLING: {
-            // The service has reached the end of its designated idle timeslice.
-            // This is not considered an error.
-            if (DEBUG) {
-                Slog.i(TAG, "Idler reached end of timeslice: " + task.who);
-            }
-            sendEndIdleTm(task.who);
-            break;
-        }
-        case VERB_ENDING: {
-            if (mCurrentIdler == task.who) {
-                if (DEBUG) {
-                    Slog.i(TAG, "Task timed out when ending; unbind needed");
-                }
-                handleIdleFinishedTm(task);
-            } else {
-                if (DEBUG) {
-                    Slog.w(TAG, "Ending timeout for non-current idle service!");
-                }
-            }
-            break;
-        }
-        default: {
-            Slog.w(TAG, "Unknown timeout state " + task.verb);
-            break;
-        }
-        }
-    }
-
-    void handleOpCompleteTm(ActiveTask task, int result) {
-        if (DEBUG) {
-            Slog.i(TAG, "handleOpComplete : task=" + task + " result=" + result);
-        }
-        if (task.verb == VERB_IDLING) {
-            // If the service was told to begin idling and responded positively, then
-            // it has begun idling and will eventually either explicitly finish, or
-            // reach the end of its allotted timeslice.  It's running free now, so we
-            // just schedule the idle-expiration timeout under the token it's already been
-            // given and let it keep going.
-            if (result != 0) {
-                scheduleOpTimeoutTm(task);
-            } else {
-                // The idle service has indicated that it does not, in fact,
-                // need to run at present, so we immediately indicate that it's
-                // to finish idling, and go on to the next idler.
-                if (DEBUG) {
-                    Slog.i(TAG, "Idler declined idling; moving along");
-                }
-                sendEndIdleTm(task.who);
-            }
-        } else {
-            // In the idling case, the task will be cleared either as the result of a timeout
-            // or of an explicit idleFinished().  For all other operations (binding, ending) we
-            // are done with the task as such, so we remove it from our bookkeeping.
-            if (DEBUG) {
-                Slog.i(TAG, "Clearing task " + task);
-            }
-            mPendingOperations.remove(task.token);
-            if (task.verb == VERB_ENDING) {
-                // The last bit of handshaking around idle cessation for this target
-                handleIdleFinishedTm(task);
-            }
-        }
-    }
-
-    void handleIdleFinishedTm(ActiveTask task) {
-        final IdleServiceInfo who = task.who;
-        if (who == mCurrentIdler) {
-            if (DEBUG) {
-                Slog.i(TAG, "Current idler has finished: " + who);
-                Slog.i(TAG, "Attributing wakelock to system work source");
-            }
-            mContext.unbindService(mConnection);
-            startNextIdleServiceTm();
-        } else {
-            Slog.w(TAG, "finish from non-current idle service? " + who);
-        }
-    }
-
-    void updateIdleServiceQueueTm() {
-        if (DEBUG) {
-            Slog.i(TAG, "Updating idle service queue");
-        }
-        PackageManager pm = mContext.getPackageManager();
-        Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE);
-        List<ResolveInfo> services = pm.queryIntentServices(idleIntent, 0);
-        for (ResolveInfo info : services) {
-            if (info.serviceInfo != null) {
-                if (IdleService.PERMISSION_BIND.equals(info.serviceInfo.permission)) {
-                    final ComponentName componentName = new ComponentName(
-                            info.serviceInfo.packageName,
-                            info.serviceInfo.name);
-                    if (DEBUG) {
-                        Slog.i(TAG, "   - " + componentName);
-                    }
-                    if (!mIdleServices.containsKey(componentName)) {
-                        if (DEBUG) {
-                            Slog.i(TAG, "      + not known; adding");
-                        }
-                        IdleServiceInfo serviceInfo = new IdleServiceInfo(info, componentName);
-                        mIdleServices.put(componentName, serviceInfo);
-                        mIdleServiceQueue.add(serviceInfo);
-                    }
-                } else {
-                    if (DEBUG) {
-                        Slog.i(TAG, "Idle service " + info.serviceInfo
-                                + " does not have required permission; ignoring");
-                    }
-                }
-            }
-        }
-    }
-
-    void startNextIdleServiceTm() {
-        mWakeLock.setWorkSource(mSystemWorkSource);
-
-        if (mLastIdler == null) {
-            // we've run the queue; nothing more to do until the next idle interval.
-            if (DEBUG) {
-                Slog.i(TAG, "Queue already drained; nothing more to do");
-            }
-            return;
-        }
-
-        if (DEBUG) {
-            Slog.i(TAG, "startNextIdleService : last=" + mLastIdler + " cur=" + mCurrentIdler);
-            if (mIdleServiceQueue.size() > 0) {
-                int i = 0;
-                Slog.i(TAG, "Queue (" + mIdleServiceQueue.size() + "):");
-                for (IdleServiceInfo info : mIdleServiceQueue) {
-                    Slog.i(TAG, "   " + i + " : " + info);
-                    i++;
-                }
-            }
-        }
-        if (mCurrentIdler != mLastIdler) {
-            if (mIdleServiceQueue.size() > 0) {
-                IdleServiceInfo target = mIdleServiceQueue.pop();
-                if (DEBUG) {
-                    Slog.i(TAG, "starting next idle service " + target);
-                }
-                Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE);
-                idleIntent.setComponent(target.componentName);
-                mCurrentIdler = target;
-                ActiveTask task = new ActiveTask(target, VERB_BINDING);
-                scheduleOpTimeoutTm(task);
-                boolean bindOk = mContext.bindServiceAsUser(idleIntent, mConnection,
-                        Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY, UserHandle.OWNER);
-                if (!bindOk) {
-                    if (DEBUG) {
-                        Slog.w(TAG, "bindService() to " + target.componentName
-                                + " failed");
-                    }
-                } else {
-                    mIdleServiceQueue.add(target);  // at the end for next time
-                    if (DEBUG) { Slog.i(TAG, "Attributing wakelock to target uid " + target.uid); }
-                    mWakeLock.setWorkSource(new WorkSource(target.uid));
-                }
-            } else {
-                // Queue is empty but mLastIdler is non-null -- eeep.  Clear *everything*
-                // and wind up until the next time around.
-                Slog.e(TAG, "Queue unexpectedly empty; resetting.  last="
-                        + mLastIdler + " cur=" + mCurrentIdler);
-                mHandler.removeMessages(MSG_TIMEOUT);
-                mPendingOperations.clear();
-                stopIdleMaintenanceTm();
-            }
-        } else {
-            // we've reached the place we started, so mark the queue as drained
-            if (DEBUG) {
-                Slog.i(TAG, "Reached end of queue.");
-            }
-            stopIdleMaintenanceTm();
-        }
-    }
-
-    void sendStartIdleTm(IdleServiceInfo who) {
-        ActiveTask task = new ActiveTask(who, VERB_IDLING);
-        scheduleOpTimeoutTm(task);
-        try {
-            who.service.startIdleMaintenance(mCallback, task.token);
-        } catch (RemoteException e) {
-            // We bound to it, but now we can't reach it.  Bail and go on to the
-            // next service.
-            mContext.unbindService(mConnection);
-            if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
-            mHandler.removeMessages(MSG_TIMEOUT);
-            startNextIdleServiceTm();
-        }
-    }
-
-    void sendEndIdleTm(IdleServiceInfo who) {
-        ActiveTask task = new ActiveTask(who, VERB_ENDING);
-        scheduleOpTimeoutTm(task);
-        if (DEBUG) {
-            Slog.i(TAG, "Sending end-idle to " + who);
-        }
-        try {
-            who.service.stopIdleMaintenance(mCallback, task.token);
-        } catch (RemoteException e) {
-            // We bound to it, but now we can't reach it.  Bail and go on to the
-            // next service.
-            mContext.unbindService(mConnection);
-            if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
-            mHandler.removeMessages(MSG_TIMEOUT);
-            startNextIdleServiceTm();
-        }
-    }
-
-    ServiceConnection mConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            if (DEBUG) {
-                Slog.i(TAG, "onServiceConnected(" + name + ")");
-            }
-            IdleServiceInfo info = mIdleServices.get(name);
-            if (info != null) {
-                // Bound!  Cancel the bind timeout
-                mHandler.removeMessages(MSG_TIMEOUT);
-                // Now tell it to start its idle work
-                info.service = IIdleService.Stub.asInterface(service);
-                sendStartIdleTm(info);
-            } else {
-                // We bound to a service we don't know about.  That's ungood.
-                Slog.e(TAG, "Connected to unexpected component " + name);
-                mContext.unbindService(this);
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            if (DEBUG) {
-                Slog.i(TAG, "onServiceDisconnected(" + name + ")");
-            }
-            IdleServiceInfo who = mIdleServices.get(name);
-            if (who == mCurrentIdler) {
-                // Hm, okay; they didn't tell us they were finished but they
-                // went away.  Crashed, probably.  Oh well.  They're gone, so
-                // we can't finish them cleanly; just force things along.
-                Slog.w(TAG, "Idler unexpectedly vanished: " + mCurrentIdler);
-                mContext.unbindService(this);
-                mHandler.removeMessages(MSG_TIMEOUT);
-                startNextIdleServiceTm();
-            } else {
-                // Not the current idler, so we don't interrupt our process...
-                if (DEBUG) {
-                    Slog.w(TAG, "Disconnect of abandoned or unexpected service " + name);
-                }
-            }
-        }
-    };
-
-    // Schedules a timeout / end-of-work based on the task verb
-    void scheduleOpTimeoutTm(ActiveTask task) {
-        final long timeoutMillis = (task.verb == VERB_IDLING) ? IDLE_TIMESLICE : OP_TIMEOUT;
-        if (DEBUG) {
-            Slog.i(TAG, "Scheduling timeout (token " + task.token
-                    + " : verb " + task.verb + ") for " + task + " in " + timeoutMillis);
-        }
-        mPendingOperations.put(task.token, task);
-        mHandler.removeMessages(MSG_TIMEOUT);
-        final Message msg = mHandler.obtainMessage(MSG_TIMEOUT, 0, task.token);
-        mHandler.sendMessageDelayed(msg, timeoutMillis);
-    }
-
-    // -------------------------------------------------------------------------------
-    public IdleMaintenanceService(Context context, BatteryService batteryService) {
-        mContext = context;
-        mBatteryService = batteryService;
-
-        mAlarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-
-        PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-
-        mHandler = new IdleHandler(mContext.getMainLooper());
-        mCallback = new IdleCallback();
-
-        Intent intent = new Intent(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
-        intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mUpdateIdleMaintenanceStatePendingIntent = PendingIntent.getBroadcast(mContext, 0,
-                intent, PendingIntent.FLAG_UPDATE_CURRENT);
-
-        register(mHandler);
-    }
-
-    public void register(Handler handler) {
-        IntentFilter intentFilter = new IntentFilter();
-
-        // Alarm actions.
-        intentFilter.addAction(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
-
-        // Battery actions.
-        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
-
-        // Screen actions.
-        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
-        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
-
-        // Dream actions.
-        intentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
-        intentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
-
-        mContext.registerReceiverAsUser(this, UserHandle.ALL,
-                intentFilter, null, mHandler);
-
-        intentFilter = new IntentFilter();
-        intentFilter.addAction(ACTION_FORCE_IDLE_MAINTENANCE);
-        mContext.registerReceiverAsUser(this, UserHandle.ALL,
-                intentFilter, android.Manifest.permission.SET_ACTIVITY_WATCHER, mHandler);
-    }
-
-    private void scheduleUpdateIdleMaintenanceState(long delayMillis) {
-        final long triggetRealTimeMillis = SystemClock.elapsedRealtime() + delayMillis;
-        mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggetRealTimeMillis,
-                mUpdateIdleMaintenanceStatePendingIntent);
-    }
-
-    private void unscheduleUpdateIdleMaintenanceState() {
-        mAlarmService.cancel(mUpdateIdleMaintenanceStatePendingIntent);
-    }
-
-    private void updateIdleMaintenanceStateTm(boolean noisy) {
-        if (mIdleMaintenanceStarted) {
-            // Idle maintenance can be interrupted by user activity, or duration
-            // time out, or low battery.
-            final boolean batteryOk
-                    = batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning();
-            if (!lastUserActivityPermitsIdleMaintenanceRunning() || !batteryOk) {
-                unscheduleUpdateIdleMaintenanceState();
-                mIdleMaintenanceStarted = false;
-                // We stopped since we don't have enough battery or timed out but the
-                // user is not using the device, so we should be able to run maintenance
-                // in the next maintenance window since the battery may be charged
-                // without interaction and the min interval between maintenances passed.
-                if (!batteryOk) {
-                    scheduleUpdateIdleMaintenanceState(
-                            getNextIdleMaintenanceIntervalStartFromNow());
-                }
-
-                EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),
-                        mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
-                        isBatteryCharging() ? 1 : 0);
-                scheduleIdleFinishTm();
-            }
-        } else if (deviceStatePermitsIdleMaintenanceStart(noisy)
-                && lastUserActivityPermitsIdleMaintenanceStart(noisy)
-                && lastRunPermitsIdleMaintenanceStart(noisy)) {
-            // Now that we started idle maintenance, we should schedule another
-            // update for the moment when the idle maintenance times out.
-            scheduleUpdateIdleMaintenanceState(MAX_IDLE_MAINTENANCE_DURATION);
-            mIdleMaintenanceStarted = true;
-            EventLogTags.writeIdleMaintenanceWindowStart(SystemClock.elapsedRealtime(),
-                    mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
-                    isBatteryCharging() ? 1 : 0);
-            mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
-            startIdleMaintenanceTm();
-        } else if (lastUserActivityPermitsIdleMaintenanceStart(noisy)) {
-             if (lastRunPermitsIdleMaintenanceStart(noisy)) {
-                // The user does not use the device and we did not run maintenance in more
-                // than the min interval between runs, so schedule an update - maybe the
-                // battery will be charged latter.
-                scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
-             } else {
-                 // The user does not use the device but we have run maintenance in the min
-                 // interval between runs, so schedule an update after the min interval ends.
-                 scheduleUpdateIdleMaintenanceState(
-                         getNextIdleMaintenanceIntervalStartFromNow());
-             }
-        }
-    }
-
-    void startIdleMaintenanceTm() {
-        if (DEBUG) {
-            Slog.i(TAG, "*** Starting idle maintenance ***");
-        }
-        if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
-        mWakeLock.setWorkSource(mSystemWorkSource);
-        mWakeLock.acquire();
-        updateIdleServiceQueueTm();
-        mCurrentIdler = null;
-        mLastIdler = (mIdleServiceQueue.size() > 0) ? mIdleServiceQueue.peekLast() : null;
-        startNextIdleServiceTm();
-    }
-
-    // Start a graceful wind-down of the idle maintenance state: end the current idler
-    // and pretend that we've finished running the queue.  If there's no current idler,
-    // this is a no-op.
-    void scheduleIdleFinishTm() {
-        if (mCurrentIdler != null) {
-            if (DEBUG) {
-                Slog.i(TAG, "*** Finishing idle maintenance ***");
-            }
-            mLastIdler = mCurrentIdler;
-            sendEndIdleTm(mCurrentIdler);
-        } else {
-            if (DEBUG) {
-                Slog.w(TAG, "Asked to finish idle maintenance but we're done already");
-            }
-        }
-    }
-
-    // Actual finalization of the idle maintenance sequence
-    void stopIdleMaintenanceTm() {
-        if (mLastIdler != null) {
-            if (DEBUG) {
-                Slog.i(TAG, "*** Idle maintenance shutdown ***");
-            }
-            mWakeLock.setWorkSource(mSystemWorkSource);
-            mLastIdler = mCurrentIdler = null;
-            updateIdleMaintenanceStateTm(false);   // resets 'started' and schedules next window
-            mWakeLock.release();
-        } else {
-            Slog.e(TAG, "ERROR: idle shutdown but invariants not held.  last=" + mLastIdler
-                    + " cur=" + mCurrentIdler + " size=" + mIdleServiceQueue.size());
-        }
-    }
-
-    private long getNextIdleMaintenanceIntervalStartFromNow() {
-        return mLastIdleMaintenanceStartTimeMillis + MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS
-                - SystemClock.elapsedRealtime();
-    }
-
-    private boolean deviceStatePermitsIdleMaintenanceStart(boolean noisy) {
-        final int minBatteryLevel = isBatteryCharging()
-                ? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING
-                : MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING;
-        boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
-                && mBatteryService.getBatteryLevel() > minBatteryLevel);
-        if (!allowed && noisy) {
-            Slog.i("IdleMaintenance", "Idle maintenance not allowed due to power");
-        }
-        return allowed;
-    }
-
-    private boolean lastUserActivityPermitsIdleMaintenanceStart(boolean noisy) {
-        // The last time the user poked the device is above the threshold.
-        boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
-                && SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
-                    > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
-        if (!allowed && noisy) {
-            Slog.i("IdleMaintenance", "Idle maintenance not allowed due to last user activity");
-        }
-        return allowed;
-    }
-
-    private boolean lastRunPermitsIdleMaintenanceStart(boolean noisy) {
-        // Enough time passed since the last maintenance run.
-        boolean allowed = SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis
-                > MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
-        if (!allowed && noisy) {
-            Slog.i("IdleMaintenance", "Idle maintenance not allowed due time since last");
-        }
-        return allowed;
-    }
-
-    private boolean lastUserActivityPermitsIdleMaintenanceRunning() {
-        // The user is not using the device.
-        return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID);
-    }
-
-    private boolean batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning() {
-        // Battery not too low and the maintenance duration did not timeout.
-        return (mBatteryService.getBatteryLevel() > MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING
-                && mLastIdleMaintenanceStartTimeMillis + MAX_IDLE_MAINTENANCE_DURATION
-                        > SystemClock.elapsedRealtime());
-    }
-
-    private boolean isBatteryCharging() {
-        return mBatteryService.getPlugType() > 0
-                && mBatteryService.getInvalidCharger() == 0;
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (DEBUG) {
-            Log.i(TAG, intent.getAction());
-        }
-        String action = intent.getAction();
-        if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
-            // We care about battery only if maintenance is in progress so we can
-            // stop it if battery is too low. Note that here we assume that the
-            // maintenance clients are properly holding a wake lock. We will
-            // refactor the maintenance to use services instead of intents for the
-            // next release. The only client for this for now is internal an holds
-            // a wake lock correctly.
-            if (mIdleMaintenanceStarted) {
-                updateIdleMaintenanceStateTm(false);
-            }
-        } else if (Intent.ACTION_SCREEN_ON.equals(action)
-                || Intent.ACTION_DREAMING_STOPPED.equals(action)) {
-            mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
-            // Unschedule any future updates since we already know that maintenance
-            // cannot be performed since the user is back.
-            unscheduleUpdateIdleMaintenanceState();
-            // If the screen went on/stopped dreaming, we know the user is using the
-            // device which means that idle maintenance should be stopped if running.
-            updateIdleMaintenanceStateTm(false);
-        } else if (Intent.ACTION_SCREEN_OFF.equals(action)
-                || Intent.ACTION_DREAMING_STARTED.equals(action)) {
-            mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
-            // If screen went off/started dreaming, we may be able to start idle maintenance
-            // after the minimal user inactivity elapses. We schedule an alarm for when
-            // this timeout elapses since the device may go to sleep by then.
-            scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
-        } else if (ACTION_UPDATE_IDLE_MAINTENANCE_STATE.equals(action)) {
-            updateIdleMaintenanceStateTm(false);
-        } else if (ACTION_FORCE_IDLE_MAINTENANCE.equals(action)) {
-            long now = SystemClock.elapsedRealtime() - 1;
-            mLastUserActivityElapsedTimeMillis = now - MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START;
-            mLastIdleMaintenanceStartTimeMillis = now - MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
-            updateIdleMaintenanceStateTm(true);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index f2db791..2a7b4f6 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -16,12 +16,13 @@
 
 package com.android.server.pm;
 
-import android.content.BroadcastReceiver;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.HashSet;
@@ -30,62 +31,63 @@ import java.util.concurrent.atomic.AtomicBoolean;
 /**
  * {@hide}
  */
-public class BackgroundDexOptService {
-
+public class BackgroundDexOptService extends JobService {
     static final String TAG = "BackgroundDexOptService";
 
-    private final BroadcastReceiver mIdleMaintenanceReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)) {
-                onIdleStart();
-            } else if (Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
-                onIdleStop();
-            }
-        }
-    };
-
-    final PackageManagerService mPackageManager;
+    static final int BACKGROUND_DEXOPT_JOB = 808;
+    private static ComponentName sDexoptServiceName = new ComponentName(
+            BackgroundDexOptService.class.getPackage().getName(),
+            BackgroundDexOptService.class.getName());
 
     final AtomicBoolean mIdleTime = new AtomicBoolean(false);
 
-    public BackgroundDexOptService(Context context) {
-        mPackageManager = (PackageManagerService)ServiceManager.getService("package");
-
-        IntentFilter idleMaintenanceFilter = new IntentFilter();
-        idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_START);
-        idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_END);
-        context.registerReceiverAsUser(mIdleMaintenanceReceiver, UserHandle.ALL,
-                                       idleMaintenanceFilter, null, null);
+    public static void schedule(Context context) {
+        JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+        JobInfo job = new JobInfo.Builder(BACKGROUND_DEXOPT_JOB, sDexoptServiceName)
+                .setRequiresDeviceIdle(true)
+                .setRequiresCharging(true)
+                .build();
+        js.schedule(job);
     }
 
-    public boolean onIdleStart() {
+    @Override
+    public boolean onStartJob(JobParameters params) {
         Log.i(TAG, "onIdleStart");
-        if (mPackageManager.isStorageLow()) {
+        final PackageManagerService pm =
+                (PackageManagerService)ServiceManager.getService("package");
+
+        if (pm.isStorageLow()) {
             return false;
         }
-        final HashSet<String> pkgs = mPackageManager.getPackagesThatNeedDexOpt();
+        final HashSet<String> pkgs = pm.getPackagesThatNeedDexOpt();
         if (pkgs == null) {
             return false;
         }
+
+        final JobParameters jobParams = params;
         mIdleTime.set(true);
         new Thread("BackgroundDexOptService_DexOpter") {
             @Override
             public void run() {
                 for (String pkg : pkgs) {
                     if (!mIdleTime.get()) {
-                        break;
+                        // stopped while still working, so we need to reschedule
+                        schedule(BackgroundDexOptService.this);
+                        return;
                     }
-                    mPackageManager.performDexOpt(pkg, false);
+                    pm.performDexOpt(pkg, false);
                 }
+                // ran to completion, so we abandon our timeslice and do not reschedule
+                jobFinished(jobParams, false);
             }
         }.start();
         return true;
     }
 
-    public void onIdleStop() {
+    @Override
+    public boolean onStopJob(JobParameters params) {
         Log.i(TAG, "onIdleStop");
         mIdleTime.set(false);
+        return false;
     }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3102cce..04ba2a1 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -933,13 +933,6 @@ public final class SystemServer {
             }
 
             try {
-                Slog.i(TAG, "IdleMaintenanceService");
-                new IdleMaintenanceService(context, battery);
-            } catch (Throwable e) {
-                reportWtf("starting IdleMaintenanceService", e);
-            }
-
-            try {
                 if (pm.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
                     mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
                 }
@@ -990,7 +983,7 @@ public final class SystemServer {
 
                 try {
                     Slog.i(TAG, "BackgroundDexOptService");
-                    new BackgroundDexOptService(context);
+                    BackgroundDexOptService.schedule(context);
                 } catch (Throwable e) {
                     reportWtf("starting BackgroundDexOptService", e);
                 }
-- 
cgit v1.1