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
diff --git a/Android.mk b/Android.mk
index 3a3756b..bc9eb3e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -94,8 +94,6 @@
 	core/java/android/app/backup/IFullBackupRestoreObserver.aidl \
 	core/java/android/app/backup/IRestoreObserver.aidl \
 	core/java/android/app/backup/IRestoreSession.aidl \
-	core/java/android/app/maintenance/IIdleCallback.aidl \
-	core/java/android/app/maintenance/IIdleService.aidl \
 	core/java/android/bluetooth/IBluetooth.aidl \
 	core/java/android/bluetooth/IBluetoothA2dp.aidl \
 	core/java/android/bluetooth/IBluetoothA2dpSink.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 5d92792..1968a78 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -34,9 +34,9 @@
 # made today requires touching the same file, just copy the old
 # touch step and add it to the end of the list.
 #
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
+# *****************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER
+# *****************************************************************
 
 # For example:
 #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
@@ -194,10 +194,15 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/wearable)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/tv/ITv*)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates)
-
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/task)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app/task)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/TaskManager)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/maintenance)
+
+# ******************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
+# ******************************************************************
diff --git a/core/java/android/app/maintenance/IIdleCallback.aidl b/core/java/android/app/maintenance/IIdleCallback.aidl
deleted file mode 100644
index 582dede..0000000
--- a/core/java/android/app/maintenance/IIdleCallback.aidl
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * Copyright 2014, 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 android.app.maintenance;
-
-import android.app.maintenance.IIdleService;
-
-/**
- * The server side of the idle maintenance IPC protocols.  The app-side implementation
- * invokes on this interface to indicate completion of the (asynchronous) instructions
- * issued by the server.
- *
- * In all cases, the 'who' parameter is the caller's service binder, used to track
- * which idle service instance is reporting.
- *
- * {@hide}
- */
-interface IIdleCallback {
-    /**
-     * Acknowledge receipt and processing of the asynchronous "start idle work" incall.
-     * 'result' is true if the app wants some time to perform ongoing background
-     * idle-time work; or false if the app declares that it does not need any time
-     * for such work.
-     */
-    void acknowledgeStart(int token, boolean result);
-
-    /**
-     * Acknowledge receipt and processing of the asynchronous "stop idle work" incall.
-     */
-    void acknowledgeStop(int token);
-
-    /*
-     * Tell the idle service manager that we're done with our idle maintenance, so that
-     * it can go on to the next one and stop attributing wakelock time to us etc.
-     *
-     * @param opToken The identifier passed in the startIdleMaintenance() call that
-     *        indicated the beginning of this service's idle timeslice.
-     */
-    void idleFinished(int token);
-}
diff --git a/core/java/android/app/maintenance/IIdleService.aidl b/core/java/android/app/maintenance/IIdleService.aidl
deleted file mode 100644
index 54abccd..0000000
--- a/core/java/android/app/maintenance/IIdleService.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * Copyright 2014, 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 android.app.maintenance;
-
-import android.app.maintenance.IIdleCallback;
-
-/**
- * Interface that the framework uses to communicate with application code
- * that implements an idle-time "maintenance" service.  End user code does
- * not implement this interface directly; instead, the app's idle service
- * implementation will extend android.app.maintenance.IdleService.
- * {@hide}
- */
-oneway interface IIdleService {
-    /**
-     * Begin your idle-time work.
-     */
-    void startIdleMaintenance(IIdleCallback callbackBinder, int token);
-    void stopIdleMaintenance(IIdleCallback callbackBinder, int token);
-}
diff --git a/core/java/android/app/maintenance/IdleService.java b/core/java/android/app/maintenance/IdleService.java
deleted file mode 100644
index 2331b81..0000000
--- a/core/java/android/app/maintenance/IdleService.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2014 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 android.app.maintenance;
-
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.app.Service;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.Slog;
-
-/**
- * Idle maintenance API.  Full docs TBW (to be written).
- */
-public abstract class IdleService extends Service {
-    private static final String TAG = "IdleService";
-
-    static final int MSG_START = 1;
-    static final int MSG_STOP = 2;
-    static final int MSG_FINISH = 3;
-
-    IdleHandler mHandler;
-    IIdleCallback mCallbackBinder;
-    int mToken;
-    final Object mHandlerLock = new Object();
-
-    void ensureHandler() {
-        synchronized (mHandlerLock) {
-            if (mHandler == null) {
-                mHandler = new IdleHandler(getMainLooper());
-            }
-        }
-    }
-
-    /**
-     * TBW: the idle service should supply an intent-filter handling this intent
-     * <p>
-     * <p class="note">The application must also protect the idle service with the
-     * {@code "android.permission.BIND_IDLE_SERVICE"} permission to ensure that other
-     * applications cannot maliciously bind to it.  If an idle service's manifest
-     * declaration does not require that permission, it will never be invoked.
-     * </p>
-     */
-    @SdkConstant(SdkConstantType.SERVICE_ACTION)
-    public static final String SERVICE_INTERFACE =
-            "android.service.idle.IdleService";
-
-    /**
-     * Idle services must be protected with this permission:
-     *
-     * <pre class="prettyprint">
-     *     <service android:name="MyIdleService"
-     *              android:permission="android.permission.BIND_IDLE_SERVICE" >
-     *         ...
-     *     </service>
-     * </pre>
-     *
-     * <p>If an idle service is declared in the manifest but not protected with this
-     * permission, that service will be ignored by the OS.
-     */
-    public static final String PERMISSION_BIND =
-            "android.permission.BIND_IDLE_SERVICE";
-
-    // Trampoline: the callbacks are always run on the main thread
-    IIdleService mBinder = new IIdleService.Stub() {
-        @Override
-        public void startIdleMaintenance(IIdleCallback callbackBinder, int token)
-                throws RemoteException {
-            ensureHandler();
-            Message msg = mHandler.obtainMessage(MSG_START, token, 0, callbackBinder);
-            mHandler.sendMessage(msg);
-        }
-
-        @Override
-        public void stopIdleMaintenance(IIdleCallback callbackBinder, int token)
-                throws RemoteException {
-            ensureHandler();
-            Message msg = mHandler.obtainMessage(MSG_STOP, token, 0, callbackBinder);
-            mHandler.sendMessage(msg);
-        }
-    };
-
-    /**
-     * Your application may begin doing "idle" maintenance work in the background.
-     * <p>
-     * Your application may continue to run in the background until it receives a call
-     * to {@link #onIdleStop()}, at which point you <i>must</i> cease doing work.  The
-     * OS will hold a wakelock on your application's behalf from the time this method is
-     * called until after the following call to {@link #onIdleStop()} returns.
-     * </p>
-     * <p>
-     * Returning {@code false} from this method indicates that you have no ongoing work
-     * to do at present.  The OS will respond by immediately calling {@link #onIdleStop()}
-     * and returning your application to its normal stopped state.  Returning {@code true}
-     * indicates that the application is indeed performing ongoing work, so the OS will
-     * let your application run in this state until it's no longer appropriate.
-     * </p>
-     * <p>
-     * You will always receive a matching call to {@link #onIdleStop()} even if your
-     * application returns {@code false} from this method.
-     *
-     * @return {@code true} to indicate that the application wishes to perform some ongoing
-     *     background work; {@code false} to indicate that it does not need to perform such
-     *     work at present.
-     */
-    public abstract boolean onIdleStart();
-
-    /**
-     * Your app's maintenance opportunity is over.  Once the application returns from
-     * this method, the wakelock held by the OS on its behalf will be released.
-     */
-    public abstract void onIdleStop();
-
-    /**
-     * Tell the OS that you have finished your idle work.  Calling this more than once,
-     * or calling it when you have not received an {@link #onIdleStart()} callback, is
-     * an error.
-     *
-     * <p>It is safe to call {@link #finishIdle()} from any thread.
-     */
-    public final void finishIdle() {
-        ensureHandler();
-        mHandler.sendEmptyMessage(MSG_FINISH);
-    }
-
-    class IdleHandler extends Handler {
-        IdleHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_START: {
-                    // Call the concrete onIdleStart(), reporting its return value back to
-                    // the OS.  If onIdleStart() throws, report it as a 'false' return but
-                    // rethrow the exception at the offending app.
-                    boolean result = false;
-                    IIdleCallback callbackBinder = (IIdleCallback) msg.obj;
-                    mCallbackBinder = callbackBinder;
-                    final int token = mToken = msg.arg1;
-                    try {
-                        result = IdleService.this.onIdleStart();
-                    } catch (Exception e) {
-                        Log.e(TAG, "Unable to start idle workload", e);
-                        throw new RuntimeException(e);
-                    } finally {
-                        // don't bother if the service already called finishIdle()
-                        if (mCallbackBinder != null) {
-                            try {
-                                callbackBinder.acknowledgeStart(token, result);
-                            } catch (RemoteException re) {
-                                Log.e(TAG, "System unreachable to start idle workload");
-                            }
-                        }
-                    }
-                    break;
-                }
-
-                case MSG_STOP: {
-                    // Structured just like MSG_START for the stop-idle bookend call.
-                    IIdleCallback callbackBinder = (IIdleCallback) msg.obj;
-                    final int token = msg.arg1;
-                    try {
-                        IdleService.this.onIdleStop();
-                    } catch (Exception e) {
-                        Log.e(TAG, "Unable to stop idle workload", e);
-                        throw new RuntimeException(e);
-                    } finally {
-                        if (mCallbackBinder != null) {
-                            try {
-                                callbackBinder.acknowledgeStop(token);
-                            } catch (RemoteException re) {
-                                Log.e(TAG, "System unreachable to stop idle workload");
-                            }
-                        }
-                    }
-                    break;
-                }
-
-                case MSG_FINISH: {
-                    if (mCallbackBinder != null) {
-                        try {
-                            mCallbackBinder.idleFinished(mToken);
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "System unreachable to finish idling");
-                        } finally {
-                            mCallbackBinder = null;
-                        }
-                    } else {
-                        Log.e(TAG, "finishIdle() called but the idle service is not started");
-                    }
-                    break;
-                }
-
-                default: {
-                    Slog.w(TAG, "Unknown message " + msg.what);
-                }
-            }
-        }
-    }
-
-    /** @hide */
-    @Override
-    public final IBinder onBind(Intent intent) {
-        return mBinder.asBinder();
-    }
-
-}
diff --git a/core/java/android/app/maintenance/package.html b/core/java/android/app/maintenance/package.html
deleted file mode 100644
index 1c9bf9d..0000000
--- a/core/java/android/app/maintenance/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body>
-    {@hide}
-</body>
-</html>
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index edca101..c08c5e2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2928,6 +2928,12 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
+        <service
+            android:name="com.android.server.pm.BackgroundDexOptService"
+            android:exported="true"
+            android:permission="android.permission.BIND_JOB_SERVICE">
+        </service>
+
     </application>
 
 </manifest>
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 @@
 /**
  * {@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 @@
             }
 
             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 @@
 
                 try {
                     Slog.i(TAG, "BackgroundDexOptService");
-                    new BackgroundDexOptService(context);
+                    BackgroundDexOptService.schedule(context);
                 } catch (Throwable e) {
                     reportWtf("starting BackgroundDexOptService", e);
                 }
diff --git a/tests/IdleServiceTest/Android.mk b/tests/IdleServiceTest/Android.mk
deleted file mode 100644
index a7879c5..0000000
--- a/tests/IdleServiceTest/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := IdleServiceTest
-LOCAL_CERTIFICATE := platform
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/tests/IdleServiceTest/AndroidManifest.xml b/tests/IdleServiceTest/AndroidManifest.xml
deleted file mode 100644
index 16d2324..0000000
--- a/tests/IdleServiceTest/AndroidManifest.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.idleservicetest">
-
-    <application>
-        <service android:name="TestService"
-                 android:exported="true"
-                 android:enabled="true"
-                 android:permission="android.permission.BIND_IDLE_SERVICE" >
-            <intent-filter>
-                <action android:name="android.service.idle.IdleService" />
-            </intent-filter>
-        </service>
-
-        <service android:name="CrashingTestService"
-                 android:exported="true"
-                 android:enabled="true"
-                 android:permission="android.permission.BIND_IDLE_SERVICE" >
-            <intent-filter>
-                <action android:name="android.service.idle.IdleService" />
-            </intent-filter>
-        </service>
-
-        <service android:name="TimeoutTestService"
-                 android:exported="true"
-                 android:enabled="true"
-                 android:permission="android.permission.BIND_IDLE_SERVICE" >
-            <intent-filter>
-                <action android:name="android.service.idle.IdleService" />
-            </intent-filter>
-        </service>
-
-        <!-- UnpermissionedTestService should never run because it does
-             not require the necessary permission in its <service> block -->
-        <service android:name="UnpermissionedTestService"
-                 android:exported="true"
-                 android:enabled="true" >
-            <intent-filter>
-                <action android:name="android.service.idle.IdleService" />
-            </intent-filter>
-        </service>
-
-    </application>
-</manifest>
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java
deleted file mode 100644
index 022ebcf..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2014 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.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.os.Handler;
-import android.util.Log;
-
-public class CrashingTestService extends IdleService {
-    static final String TAG = "CrashingTestService";
-
-    String mNull = null;
-
-    @Override
-    public boolean onIdleStart() {
-        Log.i(TAG, "Idle maintenance: onIdleStart()");
-
-        Handler h = new Handler();
-        Runnable r = new Runnable() {
-            @Override
-            public void run() {
-                Log.i(TAG, "Explicitly crashing");
-                if (mNull.equals("")) {
-                    Log.i(TAG, "won't happen");
-                }
-            }
-        };
-        Log.i(TAG, "Posting explicit crash in 15 seconds");
-        h.postDelayed(r, 15 * 1000);
-        return true;
-    }
-
-    @Override
-    public void onIdleStop() {
-        Log.i(TAG, "Idle maintenance: onIdleStop()");
-    }
-
-}
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java
deleted file mode 100644
index 7e9805f..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2014 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.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.os.Handler;
-import android.util.Log;
-
-public class TestService extends IdleService {
-    static final String TAG = "TestService";
-
-    @Override
-    public boolean onIdleStart() {
-        Log.i(TAG, "Idle maintenance: onIdleStart()");
-
-        Handler h = new Handler();
-        Runnable r = new Runnable() {
-            @Override
-            public void run() {
-                Log.i(TAG, "Explicitly finishing idle");
-                finishIdle();
-            }
-        };
-        Log.i(TAG, "Posting explicit finish in 15 seconds");
-        h.postDelayed(r, 15 * 1000);
-        return true;
-    }
-
-    @Override
-    public void onIdleStop() {
-        Log.i(TAG, "Idle maintenance: onIdleStop()");
-    }
-
-}
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java
deleted file mode 100644
index b2ba21b..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2014 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.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.util.Log;
-
-public class TimeoutTestService extends IdleService {
-    private static final String TAG = "TimeoutTestService";
-
-    @Override
-    public boolean onIdleStart() {
-        Log.i(TAG, "onIdleStart() but anticipating time-slice timeout");
-        return true;
-    }
-
-    @Override
-    public void onIdleStop() {
-        Log.i(TAG, "onIdleStop() so we're done");
-    }
-
-}
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java
deleted file mode 100644
index b9fe32b..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2014 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.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.util.Log;
-
-// Should never be invoked because its manifest declaration does not
-// require the necessary permission.
-public class UnpermissionedTestService extends IdleService {
-    private static final String TAG = "UnpermissionedTestService";
-
-    @Override
-    public boolean onIdleStart() {
-        Log.e(TAG, "onIdleStart() for this service should never be called!");
-        return false;
-    }
-
-    @Override
-    public void onIdleStop() {
-        Log.e(TAG, "onIdleStop() for this service should never be called!");
-    }
-
-}