Fix issue #21814207 and issue #21814212 (alarm manager)

Issue #21814207: AlarmManager.setAndAllowWhileIdle should also allow wake locks.

Introduce a whole new infrastructure for providing options when
sending broadcasts, much like ActivityOptions.  There is a single
option right now, asking the activity manager to apply a tempory
whitelist to each receiver of the broadcast.

Issue #21814212: Need to allow configuration of alarm manager parameters

The various alarm manager timing configurations are not modifiable
through settings, much like DeviceIdleController.  Also did a few
tweaks in the existing DeviceIdleController impl.

Change-Id: Ifd01013185acc4de668617b1e46e78e30ebed041
diff --git a/api/current.txt b/api/current.txt
index 3c1612d..ca8cb8c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3758,7 +3758,7 @@
     method public void requestUsageTimeReport(android.app.PendingIntent);
     method public android.os.Bundle toBundle();
     method public void update(android.app.ActivityOptions);
-    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.usage_time";
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
     field public static final java.lang.String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
   }
 
@@ -5194,6 +5194,7 @@
     method public void send(int, android.app.PendingIntent.OnFinished, android.os.Handler) throws android.app.PendingIntent.CanceledException;
     method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler) throws android.app.PendingIntent.CanceledException;
     method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, java.lang.String) throws android.app.PendingIntent.CanceledException;
+    method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, java.lang.String, android.os.Bundle) throws android.app.PendingIntent.CanceledException;
     method public static void writePendingIntentOrNullToParcel(android.app.PendingIntent, android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.PendingIntent> CREATOR;
diff --git a/api/system-current.txt b/api/system-current.txt
index 90d5ad3..fe8a21e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3851,7 +3851,7 @@
     method public void requestUsageTimeReport(android.app.PendingIntent);
     method public android.os.Bundle toBundle();
     method public void update(android.app.ActivityOptions);
-    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.usage_time";
+    field public static final java.lang.String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
     field public static final java.lang.String EXTRA_USAGE_TIME_REPORT_PACKAGES = "android.usage_time_packages";
   }
 
@@ -4183,6 +4183,12 @@
     method public int getWidth();
   }
 
+  public class BroadcastOptions {
+    method public static android.app.BroadcastOptions makeBasic();
+    method public void setTemporaryAppWhitelistDuration(long);
+    method public android.os.Bundle toBundle();
+  }
+
   public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
     ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
     ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
@@ -5289,6 +5295,7 @@
     method public void send(int, android.app.PendingIntent.OnFinished, android.os.Handler) throws android.app.PendingIntent.CanceledException;
     method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler) throws android.app.PendingIntent.CanceledException;
     method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, java.lang.String) throws android.app.PendingIntent.CanceledException;
+    method public void send(android.content.Context, int, android.content.Intent, android.app.PendingIntent.OnFinished, android.os.Handler, java.lang.String, android.os.Bundle) throws android.app.PendingIntent.CanceledException;
     method public static void writePendingIntentOrNullToParcel(android.app.PendingIntent, android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.PendingIntent> CREATOR;
@@ -7963,10 +7970,12 @@
     method public abstract void revokeUriPermission(android.net.Uri, int);
     method public abstract void sendBroadcast(android.content.Intent);
     method public abstract void sendBroadcast(android.content.Intent, java.lang.String);
+    method public abstract void sendBroadcast(android.content.Intent, java.lang.String, android.os.Bundle);
     method public abstract void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle);
     method public abstract void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String);
     method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String);
     method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+    method public abstract void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
     method public abstract void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
     method public abstract deprecated void sendStickyBroadcast(android.content.Intent);
     method public abstract deprecated void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
@@ -8140,10 +8149,12 @@
     method public void revokeUriPermission(android.net.Uri, int);
     method public void sendBroadcast(android.content.Intent);
     method public void sendBroadcast(android.content.Intent, java.lang.String);
+    method public void sendBroadcast(android.content.Intent, java.lang.String, android.os.Bundle);
     method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle);
     method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String);
     method public void sendOrderedBroadcast(android.content.Intent, java.lang.String);
     method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+    method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
     method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
     method public deprecated void sendStickyBroadcast(android.content.Intent);
     method public deprecated void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
@@ -34017,10 +34028,12 @@
     method public void revokeUriPermission(android.net.Uri, int);
     method public void sendBroadcast(android.content.Intent);
     method public void sendBroadcast(android.content.Intent, java.lang.String);
+    method public void sendBroadcast(android.content.Intent, java.lang.String, android.os.Bundle);
     method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle);
     method public void sendBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String);
     method public void sendOrderedBroadcast(android.content.Intent, java.lang.String);
     method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
+    method public void sendOrderedBroadcast(android.content.Intent, java.lang.String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
     method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, java.lang.String, android.content.BroadcastReceiver, android.os.Handler, int, java.lang.String, android.os.Bundle);
     method public void sendStickyBroadcast(android.content.Intent);
     method public void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index bf3b455..69ba27c 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -1002,7 +1002,7 @@
         IntentReceiver receiver = new IntentReceiver();
         System.out.println("Broadcasting: " + intent);
         mAm.broadcastIntent(null, intent, null, receiver, 0, null, null, mReceiverPermission,
-                android.app.AppOpsManager.OP_NONE, true, false, mUserId);
+                android.app.AppOpsManager.OP_NONE, null, true, false, mUserId);
         receiver.waitForFinish();
     }
 
@@ -1658,7 +1658,7 @@
         Intent intent = new Intent(
                 "com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE");
         mAm.broadcastIntent(null, intent, null, null, 0, null, null, null,
-                android.app.AppOpsManager.OP_NONE, true, false, UserHandle.USER_ALL);
+                android.app.AppOpsManager.OP_NONE, null, true, false, UserHandle.USER_ALL);
     }
 
     private void runScreenCompat() throws Exception {
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 1faf41b..2be44bc 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1812,7 +1812,7 @@
         private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
             @Override
             public int send(int code, Intent intent, String resolvedType,
-                    IIntentReceiver finishedReceiver, String requiredPermission) {
+                    IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
                 try {
                     mResult.offer(intent, 5, TimeUnit.SECONDS);
                 } catch (InterruptedException e) {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 6ae21eb..680feae 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -108,7 +108,7 @@
         try {
             getDefault().broadcastIntent(
                 null, intent, null, null, Activity.RESULT_OK, null, null,
-                null /*permission*/, appOp, false, true, userId);
+                null /*permission*/, appOp, null, false, true, userId);
         } catch (RemoteException ex) {
         }
     }
@@ -458,12 +458,13 @@
             Bundle resultExtras = data.readBundle();
             String perm = data.readString();
             int appOp = data.readInt();
+            Bundle options = data.readBundle();
             boolean serialized = data.readInt() != 0;
             boolean sticky = data.readInt() != 0;
             int userId = data.readInt();
             int res = broadcastIntent(app, intent, resolvedType, resultTo,
                     resultCode, resultData, resultExtras, perm, appOp,
-                    serialized, sticky, userId);
+                    options, serialized, sticky, userId);
             reply.writeNoException();
             reply.writeInt(res);
             return true;
@@ -2991,9 +2992,9 @@
         reply.recycle();
     }
     public int broadcastIntent(IApplicationThread caller,
-            Intent intent, String resolvedType,  IIntentReceiver resultTo,
+            Intent intent, String resolvedType, IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle map,
-            String requiredPermission, int appOp, boolean serialized,
+            String requiredPermission, int appOp, Bundle options, boolean serialized,
             boolean sticky, int userId) throws RemoteException
     {
         Parcel data = Parcel.obtain();
@@ -3008,6 +3009,7 @@
         data.writeBundle(map);
         data.writeString(requiredPermission);
         data.writeInt(appOp);
+        data.writeBundle(options);
         data.writeInt(serialized ? 1 : 0);
         data.writeInt(sticky ? 1 : 0);
         data.writeInt(userId);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 6fb997e..2406985 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -43,7 +43,7 @@
      * A long in the extras delivered by {@link #requestUsageTimeReport} that contains
      * the total time (in ms) the user spent in the app flow.
      */
-    public static final String EXTRA_USAGE_TIME_REPORT = "android.usage_time";
+    public static final String EXTRA_USAGE_TIME_REPORT = "android.activity.usage_time";
 
     /**
      * A Bundle in the extras delivered by {@link #requestUsageTimeReport} that contains
@@ -56,67 +56,67 @@
      * The package name that created the options.
      * @hide
      */
-    public static final String KEY_PACKAGE_NAME = "android:packageName";
+    public static final String KEY_PACKAGE_NAME = "android:activity.packageName";
 
     /**
      * Type of animation that arguments specify.
      * @hide
      */
-    public static final String KEY_ANIM_TYPE = "android:animType";
+    public static final String KEY_ANIM_TYPE = "android:activity.animType";
 
     /**
      * Custom enter animation resource ID.
      * @hide
      */
-    public static final String KEY_ANIM_ENTER_RES_ID = "android:animEnterRes";
+    public static final String KEY_ANIM_ENTER_RES_ID = "android:activity.animEnterRes";
 
     /**
      * Custom exit animation resource ID.
      * @hide
      */
-    public static final String KEY_ANIM_EXIT_RES_ID = "android:animExitRes";
+    public static final String KEY_ANIM_EXIT_RES_ID = "android:activity.animExitRes";
 
     /**
      * Custom in-place animation resource ID.
      * @hide
      */
-    public static final String KEY_ANIM_IN_PLACE_RES_ID = "android:animInPlaceRes";
+    public static final String KEY_ANIM_IN_PLACE_RES_ID = "android:activity.animInPlaceRes";
 
     /**
      * Bitmap for thumbnail animation.
      * @hide
      */
-    public static final String KEY_ANIM_THUMBNAIL = "android:animThumbnail";
+    public static final String KEY_ANIM_THUMBNAIL = "android:activity.animThumbnail";
 
     /**
      * Start X position of thumbnail animation.
      * @hide
      */
-    public static final String KEY_ANIM_START_X = "android:animStartX";
+    public static final String KEY_ANIM_START_X = "android:activity.animStartX";
 
     /**
      * Start Y position of thumbnail animation.
      * @hide
      */
-    public static final String KEY_ANIM_START_Y = "android:animStartY";
+    public static final String KEY_ANIM_START_Y = "android:activity.animStartY";
 
     /**
      * Initial width of the animation.
      * @hide
      */
-    public static final String KEY_ANIM_WIDTH = "android:animWidth";
+    public static final String KEY_ANIM_WIDTH = "android:activity.animWidth";
 
     /**
      * Initial height of the animation.
      * @hide
      */
-    public static final String KEY_ANIM_HEIGHT = "android:animHeight";
+    public static final String KEY_ANIM_HEIGHT = "android:activity.animHeight";
 
     /**
      * Callback for when animation is started.
      * @hide
      */
-    public static final String KEY_ANIM_START_LISTENER = "android:animStartListener";
+    public static final String KEY_ANIM_START_LISTENER = "android:activity.animStartListener";
 
     /**
      * For Activity transitions, the calling Activity's TransitionListener used to
@@ -124,15 +124,18 @@
      * complete.
      */
     private static final String KEY_TRANSITION_COMPLETE_LISTENER
-            = "android:transitionCompleteListener";
+            = "android:activity.transitionCompleteListener";
 
-    private static final String KEY_TRANSITION_IS_RETURNING = "android:transitionIsReturning";
-    private static final String KEY_TRANSITION_SHARED_ELEMENTS = "android:sharedElementNames";
-    private static final String KEY_RESULT_DATA = "android:resultData";
-    private static final String KEY_RESULT_CODE = "android:resultCode";
-    private static final String KEY_EXIT_COORDINATOR_INDEX = "android:exitCoordinatorIndex";
+    private static final String KEY_TRANSITION_IS_RETURNING
+            = "android:activity.transitionIsReturning";
+    private static final String KEY_TRANSITION_SHARED_ELEMENTS
+            = "android:activity.sharedElementNames";
+    private static final String KEY_RESULT_DATA = "android:activity.resultData";
+    private static final String KEY_RESULT_CODE = "android:activity.resultCode";
+    private static final String KEY_EXIT_COORDINATOR_INDEX
+            = "android:activity.exitCoordinatorIndex";
 
-    private static final String KEY_USAGE_TIME_REPORT = "android:usageTimeReport";
+    private static final String KEY_USAGE_TIME_REPORT = "android:activity.usageTimeReport";
 
     /** @hide */
     public static final int ANIM_NONE = 0;
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
new file mode 100644
index 0000000..1f378da
--- /dev/null
+++ b/core/java/android/app/BroadcastOptions.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 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;
+
+import android.annotation.SystemApi;
+import android.os.Bundle;
+
+/**
+ * Helper class for building an options Bundle that can be used with
+ * {@link android.content.Context#sendBroadcast(android.content.Intent)
+ * Context.sendBroadcast(Intent)} and related methods.
+ * {@hide}
+ */
+@SystemApi
+public class BroadcastOptions {
+    private long mTemporaryAppWhitelistDuration;
+
+    /**
+     * How long to temporarily put an app on the power whitelist when executing this broadcast
+     * to it.
+     * @hide
+     */
+    public static final String KEY_TEMPORARY_APP_WHITELIST_DURATION
+            = "android:broadcast.temporaryAppWhitelistDuration";
+
+    public static BroadcastOptions makeBasic() {
+        BroadcastOptions opts = new BroadcastOptions();
+        return opts;
+    }
+
+    private BroadcastOptions() {
+    }
+
+    /** @hide */
+    public BroadcastOptions(Bundle opts) {
+        mTemporaryAppWhitelistDuration = opts.getLong(KEY_TEMPORARY_APP_WHITELIST_DURATION);
+    }
+
+    /**
+     * Set a duration for which the system should temporary place an application on the
+     * power whitelist when this broadcast is being delivered to it.
+     * @param duration The duration in milliseconds; 0 means to not place on whitelist.
+     */
+    public void setTemporaryAppWhitelistDuration(long duration) {
+        mTemporaryAppWhitelistDuration = duration;
+    }
+
+    /**
+     * Return {@link #setTemporaryAppWhitelistDuration}.
+     * @hide
+     */
+    public long getTemporaryAppWhitelistDuration() {
+        return mTemporaryAppWhitelistDuration;
+    }
+
+    /**
+     * Returns the created options as a Bundle, which can be passed to
+     * {@link android.content.Context#sendBroadcast(android.content.Intent)
+     * Context.sendBroadcast(Intent)} and related methods.
+     * Note that the returned Bundle is still owned by the ActivityOptions
+     * object; you must not modify it, but can supply it to the sendBroadcast
+     * methods that take an options Bundle.
+     */
+    public Bundle toBundle() {
+        Bundle b = new Bundle();
+        if (mTemporaryAppWhitelistDuration > 0) {
+            b.putLong(KEY_TEMPORARY_APP_WHITELIST_DURATION, mTemporaryAppWhitelistDuration);
+        }
+        return b.isEmpty() ? null : b;
+    }
+}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index be36af7..0420fb6 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -46,7 +46,6 @@
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
-import android.hardware.display.DisplayManager;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -676,8 +675,8 @@
                     + " Is this really what you want?");
         }
         mMainThread.getInstrumentation().execStartActivity(
-            getOuterContext(), mMainThread.getApplicationThread(), null,
-            (Activity)null, intent, -1, options);
+                getOuterContext(), mMainThread.getApplicationThread(), null,
+                (Activity) null, intent, -1, options);
     }
 
     /** @hide */
@@ -710,8 +709,8 @@
                     + " Is this really what you want?");
         }
         mMainThread.getInstrumentation().execStartActivitiesAsUser(
-            getOuterContext(), mMainThread.getApplicationThread(), null,
-            (Activity)null, intents, options, userHandle.getIdentifier());
+                getOuterContext(), mMainThread.getApplicationThread(), null,
+                (Activity) null, intents, options, userHandle.getIdentifier());
     }
 
     @Override
@@ -724,8 +723,8 @@
                     + " Is this really what you want?");
         }
         mMainThread.getInstrumentation().execStartActivities(
-            getOuterContext(), mMainThread.getApplicationThread(), null,
-            (Activity)null, intents, options);
+                getOuterContext(), mMainThread.getApplicationThread(), null,
+                (Activity) null, intents, options);
     }
 
     @Override
@@ -766,9 +765,9 @@
         try {
             intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false,
-                getUserId());
+                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
+                    getUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
         }
@@ -781,9 +780,24 @@
         try {
             intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE,
-                false, false, getUserId());
+                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+                    Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE,
+                    null, false, false, getUserId());
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failure from system", e);
+        }
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) {
+        warnIfCallingFromSystemProcess();
+        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+        try {
+            intent.prepareToLeaveProcess();
+            ActivityManagerNative.getDefault().broadcastIntent(
+                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+                    Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE,
+                    options, false, false, getUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
         }
@@ -796,25 +810,24 @@
         try {
             intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, receiverPermission, appOp, false, false,
-                getUserId());
+                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+                    Activity.RESULT_OK, null, null, receiverPermission, appOp, null, false, false,
+                    getUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
         }
     }
 
     @Override
-    public void sendOrderedBroadcast(Intent intent,
-            String receiverPermission) {
+    public void sendOrderedBroadcast(Intent intent, String receiverPermission) {
         warnIfCallingFromSystemProcess();
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, true, false,
-                getUserId());
+                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+                    Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE,
+                    null, true, false, getUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
         }
@@ -826,7 +839,16 @@
             Handler scheduler, int initialCode, String initialData,
             Bundle initialExtras) {
         sendOrderedBroadcast(intent, receiverPermission, AppOpsManager.OP_NONE,
-                resultReceiver, scheduler, initialCode, initialData, initialExtras);
+                resultReceiver, scheduler, initialCode, initialData, initialExtras, null);
+    }
+
+    @Override
+    public void sendOrderedBroadcast(Intent intent,
+            String receiverPermission, Bundle options, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData,
+            Bundle initialExtras) {
+        sendOrderedBroadcast(intent, receiverPermission, AppOpsManager.OP_NONE,
+                resultReceiver, scheduler, initialCode, initialData, initialExtras, options);
     }
 
     @Override
@@ -834,6 +856,14 @@
             String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
             Handler scheduler, int initialCode, String initialData,
             Bundle initialExtras) {
+        sendOrderedBroadcast(intent, receiverPermission, appOp,
+                resultReceiver, scheduler, initialCode, initialData, initialExtras, null);
+    }
+
+    void sendOrderedBroadcast(Intent intent,
+            String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData,
+            Bundle initialExtras, Bundle options) {
         warnIfCallingFromSystemProcess();
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
@@ -858,7 +888,7 @@
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, receiverPermission, appOp,
-                    true, false, getUserId());
+                    options, true, false, getUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
         }
@@ -871,7 +901,7 @@
             intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(),
                     intent, resolvedType, null, Activity.RESULT_OK, null, null, null,
-                    AppOpsManager.OP_NONE, false, false, user.getIdentifier());
+                    AppOpsManager.OP_NONE, null, false, false, user.getIdentifier());
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
         }
@@ -891,7 +921,7 @@
             intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
                     mMainThread.getApplicationThread(), intent, resolvedType, null,
-                    Activity.RESULT_OK, null, null, receiverPermission, appOp, false, false,
+                    Activity.RESULT_OK, null, null, receiverPermission, appOp, null, false, false,
                     user.getIdentifier());
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
@@ -934,7 +964,7 @@
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, receiverPermission,
-                    appOp, true, false, user.getIdentifier());
+                    appOp, null, true, false, user.getIdentifier());
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
         }
@@ -949,7 +979,7 @@
             intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, true,
+                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
                 getUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
@@ -986,7 +1016,7 @@
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
-                    AppOpsManager.OP_NONE, true, true, getUserId());
+                    AppOpsManager.OP_NONE, null, true, true, getUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
         }
@@ -1017,7 +1047,7 @@
             intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
-                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, true, user.getIdentifier());
+                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true, user.getIdentifier());
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
         }
@@ -1052,7 +1082,7 @@
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
-                    AppOpsManager.OP_NONE, true, true, user.getIdentifier());
+                    AppOpsManager.OP_NONE, null, true, true, user.getIdentifier());
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
         }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 9311e5e..e7f7e13 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -107,7 +107,7 @@
     public int broadcastIntent(IApplicationThread caller, Intent intent,
             String resolvedType, IIntentReceiver resultTo, int resultCode,
             String resultData, Bundle map, String requiredPermission,
-            int appOp, boolean serialized, boolean sticky, int userId) throws RemoteException;
+            int appOp, Bundle options, boolean serialized, boolean sticky, int userId) throws RemoteException;
     public void unbroadcastIntent(IApplicationThread caller, Intent intent, int userId) throws RemoteException;
     public void finishReceiver(IBinder who, int resultCode, String resultData, Bundle map,
             boolean abortBroadcast, int flags) throws RemoteException;
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 031854a..c42ba65 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -613,7 +613,7 @@
      * is no longer allowing more intents to be sent through it.
      */
     public void send() throws CanceledException {
-        send(null, 0, null, null, null, null);
+        send(null, 0, null, null, null, null, null);
     }
 
     /**
@@ -627,7 +627,7 @@
      * is no longer allowing more intents to be sent through it.
      */
     public void send(int code) throws CanceledException {
-        send(null, code, null, null, null, null);
+        send(null, code, null, null, null, null, null);
     }
 
     /**
@@ -646,9 +646,9 @@
      * @throws CanceledException Throws CanceledException if the PendingIntent
      * is no longer allowing more intents to be sent through it.
      */
-    public void send(Context context, int code, Intent intent)
+    public void send(Context context, int code, @Nullable Intent intent)
             throws CanceledException {
-        send(context, code, intent, null, null, null);
+        send(context, code, intent, null, null, null, null);
     }
 
     /**
@@ -667,9 +667,9 @@
      * @throws CanceledException Throws CanceledException if the PendingIntent
      * is no longer allowing more intents to be sent through it.
      */
-    public void send(int code, OnFinished onFinished, Handler handler)
+    public void send(int code, @Nullable OnFinished onFinished, @Nullable Handler handler)
             throws CanceledException {
-        send(null, code, null, onFinished, handler, null);
+        send(null, code, null, onFinished, handler, null, null);
     }
 
     /**
@@ -705,9 +705,9 @@
      * @throws CanceledException Throws CanceledException if the PendingIntent
      * is no longer allowing more intents to be sent through it.
      */
-    public void send(Context context, int code, Intent intent,
-            OnFinished onFinished, Handler handler) throws CanceledException {
-        send(context, code, intent, onFinished, handler, null);
+    public void send(Context context, int code, @Nullable Intent intent,
+            @Nullable OnFinished onFinished, @Nullable Handler handler) throws CanceledException {
+        send(context, code, intent, onFinished, handler, null, null);
     }
 
     /**
@@ -748,8 +748,56 @@
      * @throws CanceledException Throws CanceledException if the PendingIntent
      * is no longer allowing more intents to be sent through it.
      */
-    public void send(Context context, int code, Intent intent,
-            OnFinished onFinished, Handler handler, String requiredPermission)
+    public void send(Context context, int code, @Nullable Intent intent,
+            @Nullable OnFinished onFinished, @Nullable Handler handler,
+            @Nullable String requiredPermission)
+            throws CanceledException {
+        send(context, code, intent, onFinished, handler, requiredPermission, null);
+    }
+
+    /**
+     * Perform the operation associated with this PendingIntent, allowing the
+     * caller to specify information about the Intent to use and be notified
+     * when the send has completed.
+     *
+     * <p>For the intent parameter, a PendingIntent
+     * often has restrictions on which fields can be supplied here, based on
+     * how the PendingIntent was retrieved in {@link #getActivity},
+     * {@link #getBroadcast}, or {@link #getService}.
+     *
+     * @param context The Context of the caller.  This may be null if
+     * <var>intent</var> is also null.
+     * @param code Result code to supply back to the PendingIntent's target.
+     * @param intent Additional Intent data.  See {@link Intent#fillIn
+     * Intent.fillIn()} for information on how this is applied to the
+     * original Intent.  Use null to not modify the original Intent.
+     * If flag {@link #FLAG_IMMUTABLE} was set when this pending intent was
+     * created, this argument will be ignored.
+     * @param onFinished The object to call back on when the send has
+     * completed, or null for no callback.
+     * @param handler Handler identifying the thread on which the callback
+     * should happen.  If null, the callback will happen from the thread
+     * pool of the process.
+     * @param requiredPermission Name of permission that a recipient of the PendingIntent
+     * is required to hold.  This is only valid for broadcast intents, and
+     * corresponds to the permission argument in
+     * {@link Context#sendBroadcast(Intent, String) Context.sendOrderedBroadcast(Intent, String)}.
+     * If null, no permission is required.
+     * @param options Additional options the caller would like to provide to modify the sending
+     * behavior.  May be built from an {@link ActivityOptions} to apply to an activity start.
+     *
+     * @see #send()
+     * @see #send(int)
+     * @see #send(Context, int, Intent)
+     * @see #send(int, android.app.PendingIntent.OnFinished, Handler)
+     * @see #send(Context, int, Intent, OnFinished, Handler)
+     *
+     * @throws CanceledException Throws CanceledException if the PendingIntent
+     * is no longer allowing more intents to be sent through it.
+     */
+    public void send(Context context, int code, @Nullable Intent intent,
+            @Nullable OnFinished onFinished, @Nullable Handler handler,
+            @Nullable String requiredPermission, @Nullable Bundle options)
             throws CanceledException {
         try {
             String resolvedType = intent != null ?
@@ -759,7 +807,7 @@
                     onFinished != null
                             ? new FinishedDispatcher(this, onFinished, handler)
                             : null,
-                    requiredPermission);
+                    requiredPermission, options);
             if (res < 0) {
                 throw new CanceledException();
             }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 83ce087..675515b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1517,6 +1517,38 @@
             @Nullable String receiverPermission);
 
     /**
+     * Broadcast the given intent to all interested BroadcastReceivers, allowing
+     * an optional required permission to be enforced.  This
+     * call is asynchronous; it returns immediately, and you will continue
+     * executing while the receivers are run.  No results are propagated from
+     * receivers and receivers can not abort the broadcast. If you want
+     * to allow receivers to propagate results or abort the broadcast, you must
+     * send an ordered broadcast using
+     * {@link #sendOrderedBroadcast(Intent, String)}.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param receiverPermission (optional) String naming a permission that
+     *               a receiver must hold in order to receive your broadcast.
+     *               If null, no permission is required.
+     * @param options (optional) Additional sending options, generated from a
+     * {@link android.app.BroadcastOptions}.
+     *
+     * @see android.content.BroadcastReceiver
+     * @see #registerReceiver
+     * @see #sendBroadcast(Intent)
+     * @see #sendOrderedBroadcast(Intent, String)
+     * @see #sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
+     * @hide
+     */
+    @SystemApi
+    public abstract void sendBroadcast(Intent intent,
+            @Nullable String receiverPermission,
+            @Nullable Bundle options);
+
+    /**
      * Like {@link #sendBroadcast(Intent, String)}, but also allows specification
      * of an associated app op as per {@link android.app.AppOpsManager}.
      * @hide
@@ -1588,11 +1620,60 @@
      * @see android.app.Activity#RESULT_OK
      */
     public abstract void sendOrderedBroadcast(@NonNull Intent intent,
-            @Nullable String receiverPermission, BroadcastReceiver resultReceiver,
+            @Nullable String receiverPermission, @Nullable BroadcastReceiver resultReceiver,
             @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
             @Nullable Bundle initialExtras);
 
     /**
+     * Version of {@link #sendBroadcast(Intent)} that allows you to
+     * receive data back from the broadcast.  This is accomplished by
+     * supplying your own BroadcastReceiver when calling, which will be
+     * treated as a final receiver at the end of the broadcast -- its
+     * {@link BroadcastReceiver#onReceive} method will be called with
+     * the result values collected from the other receivers.  The broadcast will
+     * be serialized in the same way as calling
+     * {@link #sendOrderedBroadcast(Intent, String)}.
+     *
+     * <p>Like {@link #sendBroadcast(Intent)}, this method is
+     * asynchronous; it will return before
+     * resultReceiver.onReceive() is called.
+     *
+     * <p>See {@link BroadcastReceiver} for more information on Intent broadcasts.
+     *
+     *
+     * @param intent The Intent to broadcast; all receivers matching this
+     *               Intent will receive the broadcast.
+     * @param receiverPermission String naming a permissions that
+     *               a receiver must hold in order to receive your broadcast.
+     *               If null, no permission is required.
+     * @param options (optional) Additional sending options, generated from a
+     * {@link android.app.BroadcastOptions}.
+     * @param resultReceiver Your own BroadcastReceiver to treat as the final
+     *                       receiver of the broadcast.
+     * @param scheduler A custom Handler with which to schedule the
+     *                  resultReceiver callback; if null it will be
+     *                  scheduled in the Context's main thread.
+     * @param initialCode An initial value for the result code.  Often
+     *                    Activity.RESULT_OK.
+     * @param initialData An initial value for the result data.  Often
+     *                    null.
+     * @param initialExtras An initial value for the result extras.  Often
+     *                      null.
+     * @see #sendBroadcast(Intent)
+     * @see #sendBroadcast(Intent, String)
+     * @see #sendOrderedBroadcast(Intent, String)
+     * @see android.content.BroadcastReceiver
+     * @see #registerReceiver
+     * @see android.app.Activity#RESULT_OK
+     * @hide
+     */
+    @SystemApi
+    public abstract void sendOrderedBroadcast(@NonNull Intent intent,
+            @Nullable String receiverPermission, @Nullable Bundle options,
+            @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler,
+            int initialCode, @Nullable String initialData, @Nullable Bundle initialExtras);
+
+    /**
      * Like {@link #sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler,
      * int, String, android.os.Bundle)}, but also allows specification
      * of an associated app op as per {@link android.app.AppOpsManager}.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index fb9e194..4e7c832 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import android.annotation.SystemApi;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
@@ -401,6 +402,13 @@
     }
 
     /** @hide */
+    @SystemApi
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) {
+        mBase.sendBroadcast(intent, receiverPermission, options);
+    }
+
+    /** @hide */
     @Override
     public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
         mBase.sendBroadcast(intent, receiverPermission, appOp);
@@ -423,6 +431,18 @@
     }
 
     /** @hide */
+    @SystemApi
+    @Override
+    public void sendOrderedBroadcast(
+            Intent intent, String receiverPermission, Bundle options, BroadcastReceiver resultReceiver,
+            Handler scheduler, int initialCode, String initialData,
+            Bundle initialExtras) {
+        mBase.sendOrderedBroadcast(intent, receiverPermission,
+                options, resultReceiver, scheduler, initialCode,
+                initialData, initialExtras);
+    }
+
+    /** @hide */
     @Override
     public void sendOrderedBroadcast(
         Intent intent, String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
diff --git a/core/java/android/content/IIntentSender.aidl b/core/java/android/content/IIntentSender.aidl
index 7dbd6f2..f3affa7 100644
--- a/core/java/android/content/IIntentSender.aidl
+++ b/core/java/android/content/IIntentSender.aidl
@@ -18,9 +18,10 @@
 
 import android.content.IIntentReceiver;
 import android.content.Intent;
+import android.os.Bundle;
 
 /** @hide */
 interface IIntentSender {
     int send(int code, in Intent intent, String resolvedType,
-            IIntentReceiver finishedReceiver, String requiredPermission);
+            IIntentReceiver finishedReceiver, String requiredPermission, in Bundle options);
 }
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index 166495b..13a767e 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -195,7 +195,7 @@
                     onFinished != null
                             ? new FinishedDispatcher(this, onFinished, handler)
                             : null,
-                    requiredPermission);
+                    requiredPermission, null);
             if (res < 0) {
                 throw new SendIntentException();
             }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3e9c9de..56cd1a7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7133,6 +7133,29 @@
         public static final String APP_IDLE_CONSTANTS = "app_idle_constants";
 
         /**
+         * Alarm manager specific settings.
+         * This is encoded as a key=value list, separated by commas. Ex:
+         *
+         * "min_futurity=5000,allow_while_idle_short_time=4500"
+         *
+         * The following keys are supported:
+         *
+         * <pre>
+         * min_futurity                         (long)
+         * min_interval                         (long)
+         * allow_while_idle_short_time          (long)
+         * allow_while_idle_long_time           (long)
+         * allow_while_idle_whitelist_duration  (long)
+         * </pre>
+         *
+         * <p>
+         * Type: string
+         * @hide
+         * @see com.android.server.AlarmManagerService.Constants
+         */
+        public static final String ALARM_MANAGER_CONSTANTS = "alarm_manager_constants";
+
+        /**
          * Get the key that retrieves a bluetooth headset's priority.
          * @hide
          */
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 26ece72..839b87a 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -20,13 +20,16 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.AlarmManager;
+import android.app.BroadcastOptions;
 import android.app.IAlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -43,6 +46,7 @@
 import android.text.TextUtils;
 import android.text.format.DateFormat;
 import android.util.ArrayMap;
+import android.util.KeyValueListParser;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -75,22 +79,6 @@
 import com.android.internal.util.LocalLog;
 
 class AlarmManagerService extends SystemService {
-    // The threshold for how long an alarm can be late before we print a
-    // warning message.  The time duration is in milliseconds.
-    private static final long LATE_ALARM_THRESHOLD = 10 * 1000;
-
-    // Minimum futurity of a new alarm
-    private static final long MIN_FUTURITY = 5 * 1000;  // 5 seconds, in millis
-
-    // Minimum alarm recurrence interval
-    private static final long MIN_INTERVAL = 60 * 1000;  // one minute, in millis
-
-    // Minimum time between ALLOW_WHILE_IDLE alarms when system is not idle.
-    private static final long ALLOW_WHILE_IDLE_SHORT_TIME = 60*1000;
-
-    // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling.
-    private static final long ALLOW_WHILE_IDLE_LONG_TIME = 15*60*1000;
-
     private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP;
     private static final int RTC_MASK = 1 << RTC;
     private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP;
@@ -102,7 +90,6 @@
     static final int TYPE_NONWAKEUP_MASK = 0x1; // low bit => non-wakeup
 
     static final String TAG = "AlarmManager";
-    static final String ClockReceiver_TAG = "ClockReceiver";
     static final boolean localLOGV = false;
     static final boolean DEBUG_BATCH = localLOGV || false;
     static final boolean DEBUG_VALIDATE = localLOGV || false;
@@ -148,7 +135,7 @@
     long mNextNonWakeupDeliveryTime;
     long mLastTimeChangeClockTime;
     long mLastTimeChangeRealtime;
-    long mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_SHORT_TIME;
+    long mAllowWhileIdleMinTime;
     int mNumTimeChanged;
 
     /**
@@ -157,6 +144,11 @@
      */
     final SparseLongArray mLastAllowWhileIdleDispatch = new SparseLongArray();
 
+    /**
+     * Broadcast options to use for FLAG_ALLOW_WHILE_IDLE.
+     */
+    Bundle mIdleOptions;
+
     private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser =
             new SparseArray<>();
     private final SparseArray<AlarmManager.AlarmClockInfo> mTmpSparseAlarmClockArray =
@@ -169,12 +161,137 @@
     private final SparseArray<AlarmManager.AlarmClockInfo> mHandlerSparseAlarmClockArray =
             new SparseArray<>();
 
+    /**
+     * All times are in milliseconds. These constants are kept synchronized with the system
+     * global Settings. Any access to this class or its fields should be done while
+     * holding the AlarmManagerService.mLock lock.
+     */
+    private final class Constants extends ContentObserver {
+        // Key names stored in the settings value.
+        private static final String KEY_MIN_FUTURITY = "min_futurity";
+        private static final String KEY_MIN_INTERVAL = "min_interval";
+        private static final String KEY_ALLOW_WHILE_IDLE_SHORT_TIME = "allow_while_idle_short_time";
+        private static final String KEY_ALLOW_WHILE_IDLE_LONG_TIME = "allow_while_idle_long_time";
+        private static final String KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION
+                = "allow_while_idle_whitelist_duration";
+
+        private static final long DEFAULT_MIN_FUTURITY = 5 * 1000;
+        private static final long DEFAULT_MIN_INTERVAL = 60 * 1000;
+        private static final long DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME = 60*1000;
+        private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 15*60*1000;
+        private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000;
+
+        // Minimum futurity of a new alarm
+        public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
+
+        // Minimum alarm recurrence interval
+        public long MIN_INTERVAL = DEFAULT_MIN_INTERVAL;
+
+        // Minimum time between ALLOW_WHILE_IDLE alarms when system is not idle.
+        public long ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME;
+
+        // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling.
+        public long ALLOW_WHILE_IDLE_LONG_TIME = DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME;
+
+        // BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE.
+        public long ALLOW_WHILE_IDLE_WHITELIST_DURATION
+                = DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION;
+
+        private ContentResolver mResolver;
+        private final KeyValueListParser mParser = new KeyValueListParser(',');
+        private long mLastAllowWhileIdleWhitelistDuration = -1;
+
+        public Constants(Handler handler) {
+            super(handler);
+            updateAllowWhileIdleMinTimeLocked();
+            updateAllowWhileIdleWhitelistDurationLocked();
+        }
+
+        public void start(ContentResolver resolver) {
+            mResolver = resolver;
+            mResolver.registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.ALARM_MANAGER_CONSTANTS), false, this);
+            updateConstants();
+        }
+
+        public void updateAllowWhileIdleMinTimeLocked() {
+            mAllowWhileIdleMinTime = mPendingIdleUntil != null
+                    ? ALLOW_WHILE_IDLE_LONG_TIME : ALLOW_WHILE_IDLE_SHORT_TIME;
+        }
+
+        public void updateAllowWhileIdleWhitelistDurationLocked() {
+            if (mLastAllowWhileIdleWhitelistDuration != ALLOW_WHILE_IDLE_WHITELIST_DURATION) {
+                mLastAllowWhileIdleWhitelistDuration = ALLOW_WHILE_IDLE_WHITELIST_DURATION;
+                BroadcastOptions opts = BroadcastOptions.makeBasic();
+                opts.setTemporaryAppWhitelistDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION);
+                mIdleOptions = opts.toBundle();
+            }
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            updateConstants();
+        }
+
+        private void updateConstants() {
+            synchronized (mLock) {
+                try {
+                    mParser.setString(Settings.Global.getString(mResolver,
+                            Settings.Global.ALARM_MANAGER_CONSTANTS));
+                } catch (IllegalArgumentException e) {
+                    // Failed to parse the settings string, log this and move on
+                    // with defaults.
+                    Slog.e(TAG, "Bad device idle settings", e);
+                }
+
+                MIN_FUTURITY = mParser.getLong(KEY_MIN_FUTURITY, DEFAULT_MIN_FUTURITY);
+                MIN_INTERVAL = mParser.getLong(KEY_MIN_INTERVAL, DEFAULT_MIN_INTERVAL);
+                ALLOW_WHILE_IDLE_SHORT_TIME = mParser.getLong(KEY_ALLOW_WHILE_IDLE_SHORT_TIME,
+                        DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME);
+                ALLOW_WHILE_IDLE_LONG_TIME = mParser.getLong(KEY_ALLOW_WHILE_IDLE_LONG_TIME,
+                        DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME);
+                ALLOW_WHILE_IDLE_WHITELIST_DURATION = mParser.getLong(
+                        KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION,
+                        DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION);
+
+                updateAllowWhileIdleMinTimeLocked();
+                updateAllowWhileIdleWhitelistDurationLocked();
+            }
+        }
+
+        void dump(PrintWriter pw) {
+            pw.println("  Settings:");
+
+            pw.print("    "); pw.print(KEY_MIN_FUTURITY); pw.print("=");
+            TimeUtils.formatDuration(MIN_FUTURITY, pw);
+            pw.println();
+
+            pw.print("    "); pw.print(KEY_MIN_INTERVAL); pw.print("=");
+            TimeUtils.formatDuration(MIN_INTERVAL, pw);
+            pw.println();
+
+            pw.print("    "); pw.print(KEY_ALLOW_WHILE_IDLE_SHORT_TIME); pw.print("=");
+            TimeUtils.formatDuration(ALLOW_WHILE_IDLE_SHORT_TIME, pw);
+            pw.println();
+
+            pw.print("    "); pw.print(KEY_ALLOW_WHILE_IDLE_LONG_TIME); pw.print("=");
+            TimeUtils.formatDuration(ALLOW_WHILE_IDLE_LONG_TIME, pw);
+            pw.println();
+
+            pw.print("    "); pw.print(KEY_ALLOW_WHILE_IDLE_WHITELIST_DURATION); pw.print("=");
+            TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION, pw);
+            pw.println();
+        }
+    }
+
+    final Constants mConstants;
+
     // Alarm delivery ordering bookkeeping
     static final int PRIO_TICK = 0;
     static final int PRIO_WAKEUP = 1;
     static final int PRIO_NORMAL = 2;
 
-    class PriorityClass {
+    final class PriorityClass {
         int seq;
         int priority;
 
@@ -184,11 +301,10 @@
         }
     }
 
-    final HashMap<String, PriorityClass> mPriorities =
-            new HashMap<String, PriorityClass>();
+    final HashMap<String, PriorityClass> mPriorities = new HashMap<>();
     int mCurrentSeq = 0;
 
-    class WakeupEvent {
+    static final class WakeupEvent {
         public long when;
         public int uid;
         public String action;
@@ -482,6 +598,7 @@
 
     public AlarmManagerService(Context context) {
         super(context);
+        mConstants = new Constants(mHandler);
     }
 
     static long convertToElapsed(long when, int type) {
@@ -595,7 +712,7 @@
         }
 
         // Make sure we are using the correct ALLOW_WHILE_IDLE min time.
-        mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_SHORT_TIME;
+        mConstants.updateAllowWhileIdleMinTimeLocked();
 
         // Reschedule everything.
         rescheduleKernelAlarmsLocked();
@@ -714,6 +831,13 @@
     }
 
     @Override
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_SYSTEM_SERVICES_READY) {
+            mConstants.start(getContext().getContentResolver());
+        }
+    }
+
+    @Override
     protected void finalize() throws Throwable {
         try {
             close(mNativeData);
@@ -784,11 +908,12 @@
 
         // Sanity check the recurrence interval.  This will catch people who supply
         // seconds when the API expects milliseconds.
-        if (interval > 0 && interval < MIN_INTERVAL) {
+        final long minInterval = mConstants.MIN_INTERVAL;
+        if (interval > 0 && interval < minInterval) {
             Slog.w(TAG, "Suspiciously short interval " + interval
-                    + " millis; expanding to " + (int)(MIN_INTERVAL/1000)
+                    + " millis; expanding to " + (minInterval/1000)
                     + " seconds");
-            interval = MIN_INTERVAL;
+            interval = minInterval;
         }
 
         if (type < RTC_WAKEUP || type > ELAPSED_REALTIME) {
@@ -805,7 +930,7 @@
         final long nowElapsed = SystemClock.elapsedRealtime();
         final long nominalTrigger = convertToElapsed(triggerAtTime, type);
         // Try to prevent spamming by making sure we aren't firing alarms in the immediate future
-        final long minTrigger = nowElapsed + MIN_FUTURITY;
+        final long minTrigger = nowElapsed + mConstants.MIN_FUTURITY;
         final long triggerElapsed = (nominalTrigger > minTrigger) ? nominalTrigger : minTrigger;
 
         final long maxElapsed;
@@ -904,7 +1029,7 @@
 
         if ((a.flags&AlarmManager.FLAG_IDLE_UNTIL) != 0) {
             mPendingIdleUntil = a;
-            mAllowWhileIdleMinTime = ALLOW_WHILE_IDLE_LONG_TIME;
+            mConstants.updateAllowWhileIdleMinTimeLocked();
             needRebatch = true;
         } else if ((a.flags&AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) {
             if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) {
@@ -1054,45 +1179,48 @@
     void dumpImpl(PrintWriter pw) {
         synchronized (mLock) {
             pw.println("Current Alarm Manager state:");
+            mConstants.dump(pw);
+            pw.println();
+
             final long nowRTC = System.currentTimeMillis();
             final long nowELAPSED = SystemClock.elapsedRealtime();
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
-            pw.print("nowRTC="); pw.print(nowRTC);
+            pw.print("  nowRTC="); pw.print(nowRTC);
             pw.print("="); pw.print(sdf.format(new Date(nowRTC)));
             pw.print(" nowELAPSED="); TimeUtils.formatDuration(nowELAPSED, pw);
             pw.println();
-            pw.print("mLastTimeChangeClockTime="); pw.print(mLastTimeChangeClockTime);
+            pw.print("  mLastTimeChangeClockTime="); pw.print(mLastTimeChangeClockTime);
             pw.print("="); pw.println(sdf.format(new Date(mLastTimeChangeClockTime)));
-            pw.print("mLastTimeChangeRealtime=");
+            pw.print("  mLastTimeChangeRealtime=");
             TimeUtils.formatDuration(mLastTimeChangeRealtime, pw);
             pw.println();
             if (!mInteractive) {
-                pw.print("Time since non-interactive: ");
+                pw.print("  Time since non-interactive: ");
                 TimeUtils.formatDuration(nowELAPSED - mNonInteractiveStartTime, pw);
                 pw.println();
-                pw.print("Max wakeup delay: ");
+                pw.print("  Max wakeup delay: ");
                 TimeUtils.formatDuration(currentNonWakeupFuzzLocked(nowELAPSED), pw);
                 pw.println();
-                pw.print("Time since last dispatch: ");
+                pw.print("  Time since last dispatch: ");
                 TimeUtils.formatDuration(nowELAPSED - mLastAlarmDeliveryTime, pw);
                 pw.println();
-                pw.print("Next non-wakeup delivery time: ");
+                pw.print("  Next non-wakeup delivery time: ");
                 TimeUtils.formatDuration(nowELAPSED - mNextNonWakeupDeliveryTime, pw);
                 pw.println();
             }
 
             long nextWakeupRTC = mNextWakeup + (nowRTC - nowELAPSED);
             long nextNonWakeupRTC = mNextNonWakeup + (nowRTC - nowELAPSED);
-            pw.print("Next non-wakeup alarm: ");
+            pw.print("  Next non-wakeup alarm: ");
                     TimeUtils.formatDuration(mNextNonWakeup, nowELAPSED, pw);
                     pw.print(" = "); pw.println(sdf.format(new Date(nextNonWakeupRTC)));
-            pw.print("Next wakeup: "); TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
+            pw.print("  Next wakeup: "); TimeUtils.formatDuration(mNextWakeup, nowELAPSED, pw);
                     pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC)));
-            pw.print("Num time change events: "); pw.println(mNumTimeChanged);
+            pw.print("  Num time change events: "); pw.println(mNumTimeChanged);
 
             pw.println();
-            pw.println("Next alarm clock information: ");
+            pw.println("  Next alarm clock information: ");
             final TreeSet<Integer> users = new TreeSet<>();
             for (int i = 0; i < mNextAlarmClockForUser.size(); i++) {
                 users.add(mNextAlarmClockForUser.keyAt(i));
@@ -1104,7 +1232,7 @@
                 final AlarmManager.AlarmClockInfo next = mNextAlarmClockForUser.get(user);
                 final long time = next != null ? next.getTriggerTime() : 0;
                 final boolean pendingSend = mPendingSendNextAlarmClockChangedForUser.get(user);
-                pw.print("  user:"); pw.print(user);
+                pw.print("    user:"); pw.print(user);
                 pw.print(" pendingSend:"); pw.print(pendingSend);
                 pw.print(" time:"); pw.print(time);
                 if (time > 0) {
@@ -1115,25 +1243,25 @@
             }
             if (mAlarmBatches.size() > 0) {
                 pw.println();
-                pw.print("Pending alarm batches: ");
+                pw.print("  Pending alarm batches: ");
                 pw.println(mAlarmBatches.size());
                 for (Batch b : mAlarmBatches) {
                     pw.print(b); pw.println(':');
-                    dumpAlarmList(pw, b.alarms, "  ", nowELAPSED, nowRTC, sdf);
+                    dumpAlarmList(pw, b.alarms, "    ", nowELAPSED, nowRTC, sdf);
                 }
             }
             if (mPendingIdleUntil != null || mPendingWhileIdleAlarms.size() > 0) {
                 pw.println();
-                pw.println("Idle mode state:");
-                pw.print("  Idling until: ");
+                pw.println("    Idle mode state:");
+                pw.print("      Idling until: ");
                 if (mPendingIdleUntil != null) {
                     pw.println(mPendingIdleUntil);
                     mPendingIdleUntil.dump(pw, "    ", nowRTC, nowELAPSED, sdf);
                 } else {
                     pw.println("null");
                 }
-                pw.println("  Pending alarms:");
-                dumpAlarmList(pw, mPendingWhileIdleAlarms, "    ", nowELAPSED, nowRTC, sdf);
+                pw.println("      Pending alarms:");
+                dumpAlarmList(pw, mPendingWhileIdleAlarms, "      ", nowELAPSED, nowRTC, sdf);
             }
             if (mNextWakeFromIdle != null) {
                 pw.println();
@@ -1142,17 +1270,17 @@
             }
 
             pw.println();
-            pw.print("Past-due non-wakeup alarms: ");
+            pw.print("  Past-due non-wakeup alarms: ");
             if (mPendingNonWakeupAlarms.size() > 0) {
                 pw.println(mPendingNonWakeupAlarms.size());
-                dumpAlarmList(pw, mPendingNonWakeupAlarms, "  ", nowELAPSED, nowRTC, sdf);
+                dumpAlarmList(pw, mPendingNonWakeupAlarms, "    ", nowELAPSED, nowRTC, sdf);
             } else {
                 pw.println("(none)");
             }
-            pw.print("  Number of delayed alarms: "); pw.print(mNumDelayedAlarms);
+            pw.print("    Number of delayed alarms: "); pw.print(mNumDelayedAlarms);
             pw.print(", total delay time: "); TimeUtils.formatDuration(mTotalDelayTime, pw);
             pw.println();
-            pw.print("  Max delay time: "); TimeUtils.formatDuration(mMaxDelayTime, pw);
+            pw.print("    Max delay time: "); TimeUtils.formatDuration(mMaxDelayTime, pw);
             pw.print(", max non-interactive time: ");
             TimeUtils.formatDuration(mNonInteractiveTime, pw);
             pw.println();
@@ -1161,11 +1289,11 @@
             pw.print("  Broadcast ref count: "); pw.println(mBroadcastRefCount);
             pw.println();
 
-            pw.print("mAllowWhileIdleMinTime=");
+            pw.print("  mAllowWhileIdleMinTime=");
             TimeUtils.formatDuration(mAllowWhileIdleMinTime, pw);
             pw.println();
             if (mLastAllowWhileIdleDispatch.size() > 0) {
-                pw.println("Last allow while idle dispatch times:");
+                pw.println("  Last allow while idle dispatch times:");
                 for (int i=0; i<mLastAllowWhileIdleDispatch.size(); i++) {
                     pw.print("  UID ");
                     UserHandle.formatUid(pw, mLastAllowWhileIdleDispatch.keyAt(i));
@@ -1969,6 +2097,7 @@
         mLastAlarmDeliveryTime = nowELAPSED;
         for (int i=0; i<triggerList.size(); i++) {
             Alarm alarm = triggerList.get(i);
+            final boolean allowWhileIdle = (alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0;
             try {
                 if (localLOGV) {
                     Slog.v(TAG, "sending alarm " + alarm);
@@ -1987,7 +2116,7 @@
                 alarm.operation.send(getContext(), 0,
                         mBackgroundIntent.putExtra(
                                 Intent.EXTRA_ALARM_COUNT, alarm.count),
-                        mResultReceiver, mHandler);
+                        mResultReceiver, mHandler, null, allowWhileIdle ? mIdleOptions : null);
 
                 // we have an active broadcast so stay awake.
                 if (mBroadcastRefCount == 0) {
@@ -2000,7 +2129,7 @@
                 mInFlight.add(inflight);
                 mBroadcastRefCount++;
 
-                if ((alarm.flags&AlarmManager.FLAG_ALLOW_WHILE_IDLE) != 0) {
+                if (allowWhileIdle) {
                     // Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
                     mLastAllowWhileIdleDispatch.put(alarm.uid, nowELAPSED);
                 }
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 6eba3f6..8dd087a 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -19,7 +19,6 @@
 import android.Manifest;
 import android.app.ActivityManagerNative;
 import android.app.AlarmManager;
-import android.app.AppGlobals;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -27,7 +26,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.ContentObserver;
@@ -101,10 +99,6 @@
     private static final String ACTION_ENTER_INACTIVE_STATE =
         "com.android.server.device_idle.ENTER_INACTIVE_STATE";
 
-    // TODO: These need to be moved to system settings.
-
-
-
     private AlarmManager mAlarmManager;
     private IBatteryStats mBatteryStats;
     private PowerManagerInternal mLocalPowerManager;
@@ -180,7 +174,7 @@
      * List of end times for UIDs that are temporarily marked as being allowed to access
      * the network and acquire wakelocks. Times are in milliseconds.
      */
-    private SparseLongArray mTempWhitelistAppIdEndTimes = new SparseLongArray();
+    private final SparseLongArray mTempWhitelistAppIdEndTimes = new SparseLongArray();
 
     /**
      * Current app IDs of temporarily whitelist apps for high-priority messages.
@@ -234,7 +228,7 @@
      * global Settings. Any access to this class or its fields should be done while
      * holding the DeviceIdleController lock.
      */
-    private class Constants extends ContentObserver {
+    private final class Constants extends ContentObserver {
         // Key names stored in the settings value.
         private static final String KEY_INACTIVE_TIMEOUT = "inactive_to";
         private static final String KEY_SENSING_TIMEOUT = "sensing_to";
@@ -403,49 +397,49 @@
         void dump(PrintWriter pw) {
             pw.println("  Settings:");
 
-            pw.print("    DOZE_INACTIVE_TIMEOUT=");
+            pw.print("    "); pw.print(KEY_INACTIVE_TIMEOUT); pw.print("=");
             TimeUtils.formatDuration(INACTIVE_TIMEOUT, pw);
             pw.println();
 
-            pw.print("    DOZE_SENSING_TIMEOUT=");
+            pw.print("    "); pw.print(KEY_SENSING_TIMEOUT); pw.print("=");
             TimeUtils.formatDuration(SENSING_TIMEOUT, pw);
             pw.println();
 
-            pw.print("    DOZE_MOTION_INACTIVE_TIMEOUT=");
+            pw.print("    "); pw.print(KEY_MOTION_INACTIVE_TIMEOUT); pw.print("=");
             TimeUtils.formatDuration(MOTION_INACTIVE_TIMEOUT, pw);
             pw.println();
 
-            pw.print("    DOZE_IDLE_AFTER_INACTIVE_TIMEOUT=");
+            pw.print("    "); pw.print(KEY_IDLE_AFTER_INACTIVE_TIMEOUT); pw.print("=");
             TimeUtils.formatDuration(IDLE_AFTER_INACTIVE_TIMEOUT, pw);
             pw.println();
 
-            pw.print("    DOZE_IDLE_PENDING_TIMEOUT=");
+            pw.print("    "); pw.print(KEY_IDLE_PENDING_TIMEOUT); pw.print("=");
             TimeUtils.formatDuration(IDLE_PENDING_TIMEOUT, pw);
             pw.println();
 
-            pw.print("    DOZE_MAX_IDLE_PENDING_TIMEOUT=");
+            pw.print("    "); pw.print(KEY_MAX_IDLE_PENDING_TIMEOUT); pw.print("=");
             TimeUtils.formatDuration(MAX_IDLE_PENDING_TIMEOUT, pw);
             pw.println();
 
-            pw.print("    DOZE_IDLE_PENDING_FACTOR=");
+            pw.print("    "); pw.print(KEY_IDLE_PENDING_FACTOR); pw.print("=");
             pw.println(IDLE_PENDING_FACTOR);
 
-            pw.print("    DOZE_IDLE_TIMEOUT=");
+            pw.print("    "); pw.print(KEY_IDLE_TIMEOUT); pw.print("=");
             TimeUtils.formatDuration(IDLE_TIMEOUT, pw);
             pw.println();
 
-            pw.print("    DOZE_MAX_IDLE_TIMEOUT=");
+            pw.print("    "); pw.print(KEY_MAX_IDLE_TIMEOUT); pw.print("=");
             TimeUtils.formatDuration(MAX_IDLE_TIMEOUT, pw);
             pw.println();
 
-            pw.print("    DOZE_IDLE_FACTOR=");
+            pw.print("    "); pw.print(KEY_IDLE_FACTOR); pw.print("=");
             pw.println(IDLE_FACTOR);
 
-            pw.print("    DOZE_MIN_TIME_TO_ALARM=");
+            pw.print("    "); pw.print(KEY_MIN_TIME_TO_ALARM); pw.print("=");
             TimeUtils.formatDuration(MIN_TIME_TO_ALARM, pw);
             pw.println();
 
-            pw.print("    DOZE_MAX_TEMP_APP_WHITELIST_DURATION=");
+            pw.print("    "); pw.print(KEY_MAX_TEMP_APP_WHITELIST_DURATION); pw.print("=");
             TimeUtils.formatDuration(MAX_TEMP_APP_WHITELIST_DURATION, pw);
             pw.println();
         }
@@ -465,6 +459,7 @@
             } else if (result == AnyMotionDetector.RESULT_MOVED) {
                 if (DEBUG) Slog.d(TAG, "RESULT_MOVED received.");
                 synchronized (this) {
+                    EventLogTags.writeDeviceIdle(mState, "sense_moved");
                     enterInactiveStateLocked();
                 }
             }
@@ -573,15 +568,11 @@
                     userId,
                     /*allowAll=*/ false,
                     /*requireFull=*/ false,
-                    "addAppBrieflyToWhitelist", null);
+                    "addPowerSaveTempWhitelistApp", null);
             final long token = Binder.clearCallingIdentity();
             try {
-                PackageInfo pi = AppGlobals.getPackageManager()
-                        .getPackageInfo(packageName, 0, userId);
-                if (pi == null) return;
                 DeviceIdleController.this.addPowerSaveTempWhitelistAppInternal(packageName,
                         duration, userId);
-            } catch (RemoteException re) {
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -592,6 +583,12 @@
         }
     }
 
+    public final class LocalService {
+        public void addPowerSaveTempWhitelistAppDirect(int appId, long duration) {
+            DeviceIdleController.this.addPowerSaveTempWhitelistAppDirectInternal(appId, duration);
+        }
+    }
+
     public DeviceIdleController(Context context) {
         super(context);
         mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));
@@ -635,6 +632,7 @@
         }
 
         publishBinderService(Context.DEVICE_IDLE_CONTROLLER, new BinderService());
+        publishLocalService(LocalService.class, new LocalService());
     }
 
     @Override
@@ -765,33 +763,41 @@
         try {
             int uid = getContext().getPackageManager().getPackageUid(packageName, userId);
             int appId = UserHandle.getAppId(uid);
-            final long timeNow = System.currentTimeMillis();
-            synchronized (this) {
-                duration = Math.min(duration, mConstants.MAX_TEMP_APP_WHITELIST_DURATION);
-                long currentEndTime = mTempWhitelistAppIdEndTimes.get(appId);
-                // Set the new end time
-                mTempWhitelistAppIdEndTimes.put(appId, timeNow + duration);
-                if (DEBUG) {
-                    Slog.d(TAG, "Adding AppId " + appId + " to temp whitelist");
-                }
-                if (currentEndTime == 0) {
-                    // No pending timeout for the app id, post a delayed message
-                    postTempActiveTimeoutMessage(appId, duration);
-                    updateTempWhitelistAppIdsLocked();
-                    reportTempWhitelistChangedLocked();
-                }
-            }
+            addPowerSaveTempWhitelistAppDirectInternal(appId, duration);
         } catch (NameNotFoundException e) {
         }
     }
 
+    /**
+     * Adds an app to the temporary whitelist and resets the endTime for granting the
+     * app an exemption to access network and acquire wakelocks.
+     */
+    public void addPowerSaveTempWhitelistAppDirectInternal(int appId, long duration) {
+        final long timeNow = SystemClock.elapsedRealtime();
+        synchronized (this) {
+            duration = Math.min(duration, mConstants.MAX_TEMP_APP_WHITELIST_DURATION);
+            long currentEndTime = mTempWhitelistAppIdEndTimes.get(appId);
+            // Set the new end time
+            mTempWhitelistAppIdEndTimes.put(appId, timeNow + duration);
+            if (DEBUG) {
+                Slog.d(TAG, "Adding AppId " + appId + " to temp whitelist");
+            }
+            if (currentEndTime == 0) {
+                // No pending timeout for the app id, post a delayed message
+                postTempActiveTimeoutMessage(appId, duration);
+                updateTempWhitelistAppIdsLocked();
+                reportTempWhitelistChangedLocked();
+            }
+        }
+    }
+
     private void postTempActiveTimeoutMessage(int uid, long delay) {
         mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TEMP_APP_WHITELIST_TIMEOUT, uid, 0),
                 delay);
     }
 
     void checkTempAppWhitelistTimeout(int uid) {
-        final long timeNow = System.currentTimeMillis();
+        final long timeNow = SystemClock.elapsedRealtime();
         synchronized (this) {
             long endTime = mTempWhitelistAppIdEndTimes.get(uid);
             if (endTime == 0) {
@@ -1295,6 +1301,8 @@
         }
 
         synchronized (this) {
+            mConstants.dump(pw);
+
             int size = mPowerSaveWhitelistApps.size();
             if (size > 0) {
                 pw.println("  Whitelist system apps:");
@@ -1313,17 +1321,34 @@
             }
             size = mPowerSaveWhitelistAppIds.size();
             if (size > 0) {
-                pw.println("  Whitelist app uids:");
+                pw.println("  Whitelist app ids:");
                 for (int i = 0; i < size; i++) {
-                    pw.print("    UID=");
+                    pw.print("    ");
                     pw.print(mPowerSaveWhitelistAppIds.keyAt(i));
-                    pw.print(": ");
-                    pw.print(mPowerSaveWhitelistAppIds.valueAt(i));
                     pw.println();
                 }
             }
-
-            mConstants.dump(pw);
+            size = mTempWhitelistAppIdEndTimes.size();
+            if (size > 0) {
+                pw.println("  Temp whitelist schedule:");
+                final long timeNow = SystemClock.elapsedRealtime();
+                for (int i = 0; i < size; i++) {
+                    pw.print("    UID=");
+                    pw.print(mTempWhitelistAppIdEndTimes.keyAt(i));
+                    pw.print(": ");
+                    TimeUtils.formatDuration(mTempWhitelistAppIdEndTimes.valueAt(i), timeNow, pw);
+                    pw.println();
+                }
+            }
+            size = mTempWhitelistAppIdArray != null ? mTempWhitelistAppIdArray.length : 0;
+            if (size > 0) {
+                pw.println("  Temp whitelist app ids:");
+                for (int i = 0; i < size; i++) {
+                    pw.print("    ");
+                    pw.print(mTempWhitelistAppIdArray[i]);
+                    pw.println();
+                }
+            }
 
             pw.print("  mSigMotionSensor="); pw.println(mSigMotionSensor);
             pw.print("  mCurDisplay="); pw.println(mCurDisplay);
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index 6e67970..e0a9ab4 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -34,7 +34,7 @@
  * local interfaces that other services within the system server may use to access
  * privileged internal functions.
  * <li>Then {@link #onBootPhase(int)} is called as many times as there are boot phases
- * until {@link #PHASE_BOOT_COMPLETE} is sent, which is the last boot phase. Each phase
+ * until {@link #PHASE_BOOT_COMPLETED} is sent, which is the last boot phase. Each phase
  * is an opportunity to do special work, like acquiring optional service dependencies,
  * waiting to see if SafeMode is enabled, or registering with a service that gets
  * started after this one.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 667abb6..c4f460e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -39,6 +39,7 @@
 import android.Manifest;
 import android.app.AppOpsManager;
 import android.app.ApplicationThreadNative;
+import android.app.BroadcastOptions;
 import android.app.IActivityContainer;
 import android.app.IActivityContainerCallback;
 import android.app.IAppTask;
@@ -89,6 +90,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.AppOpsService;
 import com.android.server.AttributeCache;
+import com.android.server.DeviceIdleController;
 import com.android.server.IntentResolver;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
@@ -941,6 +943,11 @@
     UsageStatsManagerInternal mUsageStatsService;
 
     /**
+     * Access to DeviceIdleController service.
+     */
+    DeviceIdleController.LocalService mLocalDeviceIdleController;
+
+    /**
      * Information about and control over application operations
      */
     final AppOpsService mAppOpsService;
@@ -1438,7 +1445,7 @@
                     }
                     broadcastIntentLocked(null, null, intent,
                             null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                            false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
+                            null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
 
                     if (mShowDialogs) {
                         Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
@@ -2559,9 +2566,9 @@
 
     @Override
     public void batterySendBroadcast(Intent intent) {
-        broadcastIntentLocked(null, null, intent, null,
-                null, 0, null, null, null, AppOpsManager.OP_NONE, false, false, -1,
-                Process.SYSTEM_UID, UserHandle.USER_ALL);
+        broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                AppOpsManager.OP_NONE, null, false, false,
+                -1, Process.SYSTEM_UID, UserHandle.USER_ALL);
     }
 
     /**
@@ -5089,7 +5096,7 @@
                         Uri.fromParts("package", packageName, null));
                 intent.putExtra(Intent.EXTRA_UID, pkgUid);
                 broadcastIntentInPackage("android", Process.SYSTEM_UID, intent,
-                        null, null, 0, null, null, null, false, false, userId);
+                        null, null, 0, null, null, null, null, false, false, userId);
             } catch (RemoteException e) {
             }
         } finally {
@@ -5322,9 +5329,9 @@
 
         mStackSupervisor.closeSystemDialogsLocked();
 
-        broadcastIntentLocked(null, null, intent, null,
-                null, 0, null, null, null, AppOpsManager.OP_NONE, false, false, -1,
-                Process.SYSTEM_UID, UserHandle.USER_ALL);
+        broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                AppOpsManager.OP_NONE, null, false, false,
+                -1, Process.SYSTEM_UID, UserHandle.USER_ALL);
     }
 
     @Override
@@ -5423,8 +5430,7 @@
         intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid));
         broadcastIntentLocked(null, null, intent,
                 null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                false, false,
-                MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid));
+                null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.getUserId(uid));
     }
 
     private void forceStopUserLocked(int userId, String reason) {
@@ -5435,8 +5441,7 @@
         intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
         broadcastIntentLocked(null, null, intent,
                 null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                false, false,
-                MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+                null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
     }
 
     private final boolean killPackageProcessesLocked(String packageName, int appId,
@@ -6329,8 +6334,8 @@
                                 },
                                 0, null, null,
                                 android.Manifest.permission.RECEIVE_BOOT_COMPLETED,
-                                AppOpsManager.OP_NONE, true, false, MY_PID, Process.SYSTEM_UID,
-                                userId);
+                                AppOpsManager.OP_NONE, null, true, false,
+                                MY_PID, Process.SYSTEM_UID, userId);
                     }
                 }
                 scheduleStartProfilesLocked();
@@ -11442,8 +11447,7 @@
             EventLogTags.writeAmPreBoot(users[curUser], intent.getComponent().getPackageName());
             broadcastIntentLocked(null, null, intent, null, this,
                     0, null, null, null, AppOpsManager.OP_NONE,
-                    true, false, MY_PID, Process.SYSTEM_UID,
-                    users[curUser]);
+                    null, true, false, MY_PID, Process.SYSTEM_UID, users[curUser]);
         }
 
         public void performReceive(Intent intent, int resultCode,
@@ -11535,6 +11539,9 @@
                 return;
             }
 
+            mLocalDeviceIdleController
+                    = LocalServices.getService(DeviceIdleController.LocalService.class);
+
             // Make sure we have the current profile info, since it is needed for
             // security checks.
             updateCurrentProfileIdsLocked();
@@ -11704,7 +11711,7 @@
                 intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId);
                 broadcastIntentLocked(null, null, intent,
                         null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                        false, false, MY_PID, Process.SYSTEM_UID, mCurrentUserId);
+                        null, false, false, MY_PID, Process.SYSTEM_UID, mCurrentUserId);
                 intent = new Intent(Intent.ACTION_USER_STARTING);
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                 intent.putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId);
@@ -11717,7 +11724,7 @@
                             }
                         }, 0, null, null,
                         INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE,
-                        true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+                        null, true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
             } catch (Throwable t) {
                 Slog.wtf(TAG, "Failed sending first user broadcasts", t);
             } finally {
@@ -16101,8 +16108,8 @@
                     Intent intent = allSticky.get(i);
                     BroadcastQueue queue = broadcastQueueForIntent(intent);
                     BroadcastRecord r = new BroadcastRecord(queue, intent, null,
-                            null, -1, -1, null, null, AppOpsManager.OP_NONE, receivers, null, 0,
-                            null, null, false, true, true, -1);
+                            null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
+                            null, 0, null, null, false, true, true, -1);
                     queue.enqueueParallelBroadcastLocked(r);
                     queue.scheduleBroadcastsLocked();
                 }
@@ -16255,9 +16262,8 @@
     private final int broadcastIntentLocked(ProcessRecord callerApp,
             String callerPackage, Intent intent, String resolvedType,
             IIntentReceiver resultTo, int resultCode, String resultData,
-            Bundle map, String requiredPermission, int appOp,
-            boolean ordered, boolean sticky, int callingPid, int callingUid,
-            int userId) {
+            Bundle resultExtras, String requiredPermission, int appOp, Bundle options,
+            boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
         intent = new Intent(intent);
 
         // By default broadcasts do not go to stopped apps.
@@ -16292,6 +16298,28 @@
             }
         }
 
+        BroadcastOptions brOptions = null;
+        if (options != null) {
+            brOptions = new BroadcastOptions(options);
+            if (brOptions.getTemporaryAppWhitelistDuration() > 0) {
+                // See if the caller is allowed to do this.  Note we are checking against
+                // the actual real caller (not whoever provided the operation as say a
+                // PendingIntent), because that who is actually supplied the arguments.
+                if (checkComponentPermission(
+                        android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST,
+                        Binder.getCallingPid(), Binder.getCallingUid(), -1, true)
+                        != PackageManager.PERMISSION_GRANTED) {
+                    String msg = "Permission Denial: " + intent.getAction()
+                            + " broadcast from " + callerPackage + " (pid=" + callingPid
+                            + ", uid=" + callingUid + ")"
+                            + " requires "
+                            + android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
+                    Slog.w(TAG, msg);
+                    throw new SecurityException(msg);
+                }
+            }
+        }
+
         /*
          * Prevent non-system code (defined here to be non-persistent
          * processes) from sending protected broadcasts.
@@ -16598,8 +16626,8 @@
             final BroadcastQueue queue = broadcastQueueForIntent(intent);
             BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                     callerPackage, callingPid, callingUid, resolvedType, requiredPermission,
-                    appOp, registeredReceivers, resultTo, resultCode, resultData, map,
-                    ordered, sticky, false, userId);
+                    appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
+                    resultExtras, ordered, sticky, false, userId);
             if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
             final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
             if (!replaced) {
@@ -16687,8 +16715,8 @@
             BroadcastQueue queue = broadcastQueueForIntent(intent);
             BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                     callerPackage, callingPid, callingUid, resolvedType,
-                    requiredPermission, appOp, receivers, resultTo, resultCode,
-                    resultData, map, ordered, sticky, false, userId);
+                    requiredPermission, appOp, brOptions, receivers, resultTo, resultCode,
+                    resultData, resultExtras, ordered, sticky, false, userId);
 
             if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
                     + ": prev had " + queue.mOrderedBroadcasts.size());
@@ -16735,8 +16763,9 @@
 
     public final int broadcastIntent(IApplicationThread caller,
             Intent intent, String resolvedType, IIntentReceiver resultTo,
-            int resultCode, String resultData, Bundle map,
-            String requiredPermission, int appOp, boolean serialized, boolean sticky, int userId) {
+            int resultCode, String resultData, Bundle resultExtras,
+            String requiredPermission, int appOp, Bundle options,
+            boolean serialized, boolean sticky, int userId) {
         enforceNotIsolatedCaller("broadcastIntent");
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
@@ -16747,8 +16776,8 @@
             final long origId = Binder.clearCallingIdentity();
             int res = broadcastIntentLocked(callerApp,
                     callerApp != null ? callerApp.info.packageName : null,
-                    intent, resolvedType, resultTo,
-                    resultCode, resultData, map, requiredPermission, appOp, serialized, sticky,
+                    intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
+                    requiredPermission, appOp, null, serialized, sticky,
                     callingPid, callingUid, userId);
             Binder.restoreCallingIdentity(origId);
             return res;
@@ -16757,15 +16786,16 @@
 
     int broadcastIntentInPackage(String packageName, int uid,
             Intent intent, String resolvedType, IIntentReceiver resultTo,
-            int resultCode, String resultData, Bundle map,
-            String requiredPermission, boolean serialized, boolean sticky, int userId) {
+            int resultCode, String resultData, Bundle resultExtras,
+            String requiredPermission, Bundle options, boolean serialized, boolean sticky,
+            int userId) {
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
 
             final long origId = Binder.clearCallingIdentity();
             int res = broadcastIntentLocked(null, packageName, intent, resolvedType,
-                    resultTo, resultCode, resultData, map, requiredPermission,
-                    AppOpsManager.OP_NONE, serialized, sticky, -1, uid, userId);
+                    resultTo, resultCode, resultData, resultExtras, requiredPermission,
+                    AppOpsManager.OP_NONE, options, serialized, sticky, -1, uid, userId);
             Binder.restoreCallingIdentity(origId);
             return res;
         }
@@ -17162,8 +17192,8 @@
                         | Intent.FLAG_RECEIVER_REPLACE_PENDING
                         | Intent.FLAG_RECEIVER_FOREGROUND);
                 broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
-                        null, AppOpsManager.OP_NONE, false, false, MY_PID,
-                        Process.SYSTEM_UID, UserHandle.USER_ALL);
+                        null, AppOpsManager.OP_NONE, null, false, false,
+                        MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
                 if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
                     intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
                     intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -17172,7 +17202,7 @@
                     }
                     broadcastIntentLocked(null, null, intent,
                             null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                            false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+                            null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
                 }
             }
         }
@@ -19642,7 +19672,7 @@
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                     broadcastIntentLocked(null, null, intent,
                             null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                            false, false, MY_PID, Process.SYSTEM_UID, userId);
+                            null, false, false, MY_PID, Process.SYSTEM_UID, userId);
                 }
 
                 if ((userInfo.flags&UserInfo.FLAG_INITIALIZED) == 0) {
@@ -19657,8 +19687,7 @@
                                         onUserInitialized(uss, foreground, oldUserId, userId);
                                     }
                                 }, 0, null, null, null, AppOpsManager.OP_NONE,
-                                true, false, MY_PID, Process.SYSTEM_UID,
-                                userId);
+                                null, true, false, MY_PID, Process.SYSTEM_UID, userId);
                         uss.initializing = true;
                     } else {
                         getUserManagerLocked().makeInitialized(userInfo.id);
@@ -19680,13 +19709,13 @@
                     broadcastIntentLocked(null, null, intent,
                             null, new IIntentReceiver.Stub() {
                                 @Override
-                                public void performReceive(Intent intent, int resultCode, String data,
-                                        Bundle extras, boolean ordered, boolean sticky, int sendingUser)
-                                        throws RemoteException {
+                                public void performReceive(Intent intent, int resultCode,
+                                        String data, Bundle extras, boolean ordered, boolean sticky,
+                                        int sendingUser) throws RemoteException {
                                 }
                             }, 0, null, null,
                             INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE,
-                            true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+                            null, true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
                 }
             }
         } finally {
@@ -19724,7 +19753,7 @@
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
                     broadcastIntentLocked(null, null, intent,
                             null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                            false, false, MY_PID, Process.SYSTEM_UID, profileUserId);
+                            null, false, false, MY_PID, Process.SYSTEM_UID, profileUserId);
                 }
             }
             if (newUserId >= 0) {
@@ -19739,7 +19768,7 @@
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
                     broadcastIntentLocked(null, null, intent,
                             null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                            false, false, MY_PID, Process.SYSTEM_UID, profileUserId);
+                            null, false, false, MY_PID, Process.SYSTEM_UID, profileUserId);
                 }
                 intent = new Intent(Intent.ACTION_USER_SWITCHED);
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
@@ -19748,7 +19777,7 @@
                 broadcastIntentLocked(null, null, intent,
                         null, null, 0, null, null,
                         android.Manifest.permission.MANAGE_USERS, AppOpsManager.OP_NONE,
-                        false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+                        null, false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -19925,7 +19954,7 @@
                 broadcastIntentLocked(null, null, intent,
                         null, null, 0, null, null,
                         android.Manifest.permission.RECEIVE_BOOT_COMPLETED, AppOpsManager.OP_NONE,
-                        true, false, MY_PID, Process.SYSTEM_UID, userId);
+                        null, true, false, MY_PID, Process.SYSTEM_UID, userId);
             }
         }
     }
@@ -20057,14 +20086,14 @@
                         mSystemServiceManager.stopUser(userId);
                         broadcastIntentLocked(null, null, shutdownIntent,
                                 null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE,
-                                true, false, MY_PID, Process.SYSTEM_UID, userId);
+                                null, true, false, MY_PID, Process.SYSTEM_UID, userId);
                     }
                 };
                 // Kick things off.
                 broadcastIntentLocked(null, null, stoppingIntent,
                         null, stoppingReceiver, 0, null, null,
                         INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE,
-                        true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+                        null, true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 80b8a93..2335071 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -26,6 +26,7 @@
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
 import android.content.ComponentName;
 import android.content.IIntentReceiver;
 import android.content.Intent;
@@ -43,6 +44,7 @@
 import android.os.UserHandle;
 import android.util.EventLog;
 import android.util.Slog;
+import com.android.server.DeviceIdleController;
 
 import static com.android.server.am.ActivityManagerDebugConfig.*;
 
@@ -146,6 +148,8 @@
 
     static final int BROADCAST_INTENT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG;
     static final int BROADCAST_TIMEOUT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 1;
+    static final int SCHEDULE_TEMP_WHITELIST_MSG
+            = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 2;
 
     final BroadcastHandler mHandler;
 
@@ -167,6 +171,13 @@
                         broadcastTimeoutLocked(true);
                     }
                 } break;
+                case SCHEDULE_TEMP_WHITELIST_MSG: {
+                    DeviceIdleController.LocalService dic = mService.mLocalDeviceIdleController;
+                    if (dic != null) {
+                        dic.addPowerSaveTempWhitelistAppDirect(UserHandle.getAppId(msg.arg1),
+                                msg.arg2);
+                    }
+                } break;
             }
         }
     };
@@ -547,6 +558,19 @@
         }
     }
 
+    final void scheduleTempWhitelistLocked(int uid, long duration) {
+        if (duration > Integer.MAX_VALUE) {
+            duration = Integer.MAX_VALUE;
+        }
+        // XXX ideally we should pause the broadcast until everything behind this is done,
+        // or else we will likely start dispatching the broadcast before we have opened
+        // access to the app (there is a lot of asynchronicity behind this).  It is probably
+        // not that big a deal, however, because the main purpose here is to allow apps
+        // to hold wake locks, and they will be able to acquire their wake lock immediately
+        // it just won't be enabled until we get through this work.
+        mHandler.obtainMessage(SCHEDULE_TEMP_WHITELIST_MSG, uid, (int)duration).sendToTarget();
+    }
+
     final void processNextBroadcast(boolean fromMsg) {
         synchronized(mService) {
             BroadcastRecord r;
@@ -721,7 +745,9 @@
                 setBroadcastTimeoutLocked(timeoutTime);
             }
 
-            Object nextReceiver = r.receivers.get(recIdx);
+            final BroadcastOptions brOptions = r.options;
+            final Object nextReceiver = r.receivers.get(recIdx);
+
             if (nextReceiver instanceof BroadcastFilter) {
                 // Simple case: this is a registered receiver who gets
                 // a direct call.
@@ -739,6 +765,11 @@
                             + r.ordered + " receiver=" + r.receiver);
                     r.state = BroadcastRecord.IDLE;
                     scheduleBroadcastsLocked();
+                } else {
+                    if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
+                        scheduleTempWhitelistLocked(filter.owningUid,
+                                brOptions.getTemporaryAppWhitelistDuration());
+                    }
                 }
                 return;
             }
@@ -882,6 +913,11 @@
                         + info.activityInfo.applicationInfo.uid);
             }
 
+            if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) {
+                scheduleTempWhitelistLocked(receiverUid,
+                        brOptions.getTemporaryAppWhitelistDuration());
+            }
+
             // Broadcast is being executed, its package can't be stopped.
             try {
                 AppGlobals.getPackageManager().setPackageStoppedState(
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index c050d03f..b943222 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
 import android.content.IIntentReceiver;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -52,6 +53,7 @@
     final String resolvedType; // the resolved data type
     final String requiredPermission; // a permission the caller has required
     final int appOp;        // an app op that is associated with this broadcast
+    final BroadcastOptions options; // BroadcastOptions supplied by caller
     final List receivers;   // contains BroadcastFilter and ResolveInfo
     IIntentReceiver resultTo; // who receives final result if non-null
     long enqueueClockTime;  // the clock time the broadcast was enqueued
@@ -105,6 +107,9 @@
             pw.print(prefix); pw.print("requiredPermission="); pw.print(requiredPermission);
                     pw.print("  appOp="); pw.println(appOp);
         }
+        if (options != null) {
+            pw.print(prefix); pw.print("options="); pw.println(options.toBundle());
+        }
         pw.print(prefix); pw.print("enqueueClockTime=");
                 pw.print(new Date(enqueueClockTime));
                 pw.print(" dispatchClockTime=");
@@ -180,8 +185,8 @@
     BroadcastRecord(BroadcastQueue _queue,
             Intent _intent, ProcessRecord _callerApp, String _callerPackage,
             int _callingPid, int _callingUid, String _resolvedType, String _requiredPermission,
-            int _appOp, List _receivers, IIntentReceiver _resultTo, int _resultCode,
-            String _resultData, Bundle _resultExtras, boolean _serialized,
+            int _appOp, BroadcastOptions _options, List _receivers, IIntentReceiver _resultTo,
+            int _resultCode, String _resultData, Bundle _resultExtras, boolean _serialized,
             boolean _sticky, boolean _initialSticky,
             int _userId) {
         queue = _queue;
@@ -194,6 +199,7 @@
         resolvedType = _resolvedType;
         requiredPermission = _requiredPermission;
         appOp = _appOp;
+        options = _options;
         receivers = _receivers;
         resultTo = _resultTo;
         resultCode = _resultCode;
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 531de46..ece3ffb 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -199,9 +199,9 @@
     }
 
     public int send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver,
-            String requiredPermission) throws TransactionTooLargeException {
+            String requiredPermission, Bundle options) throws TransactionTooLargeException {
         return sendInner(code, intent, resolvedType, finishedReceiver,
-                requiredPermission, null, null, 0, 0, 0, null, null);
+                requiredPermission, null, null, 0, 0, 0, options, null);
     }
 
     int sendInner(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver,
@@ -293,9 +293,9 @@
                             // If a completion callback has been requested, require
                             // that the broadcast be delivered synchronously
                             int sent = owner.broadcastIntentInPackage(key.packageName, uid,
-                                    finalIntent, resolvedType,
-                                    finishedReceiver, code, null, null,
-                                requiredPermission, (finishedReceiver != null), false, userId);
+                                    finalIntent, resolvedType, finishedReceiver, code, null, null,
+                                    requiredPermission, options, (finishedReceiver != null),
+                                    false, userId);
                             if (sent == ActivityManager.BROADCAST_SUCCESS) {
                                 sendFinish = false;
                             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 5b7dd70..06e27fc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8808,7 +8808,7 @@
                         }
                         am.broadcastIntent(null, intent, null, finishedReceiver,
                                 0, null, null, null, android.app.AppOpsManager.OP_NONE,
-                                finishedReceiver != null, false, id);
+                                null, finishedReceiver != null, false, id);
                     }
                 } catch (RemoteException ex) {
                 }
@@ -8982,7 +8982,7 @@
                         .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
                         .setPackage(packageName);
                 am.broadcastIntent(null, bcIntent, null, null, 0, null, null, null,
-                        android.app.AppOpsManager.OP_NONE, false, false, userId);
+                        android.app.AppOpsManager.OP_NONE, null, false, false, userId);
             }
         } catch (RemoteException e) {
             // shouldn't happen
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 75b8278..3767fce 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -981,7 +981,7 @@
         }
     }
 
-    private class BinderService extends IUsageStatsManager.Stub {
+    private final class BinderService extends IUsageStatsManager.Stub {
 
         private boolean hasPermission(String callingPackage) {
             final int callingUid = Binder.getCallingUid();
@@ -1121,7 +1121,7 @@
      * ActivityManagerService will call these methods holding the 'am' lock, which means we
      * shouldn't be doing any IO work or other long running tasks in these methods.
      */
-    private class LocalService extends UsageStatsManagerInternal {
+    private final class LocalService extends UsageStatsManagerInternal {
 
         @Override
         public void reportEvent(ComponentName component, int userId, int eventType) {
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 04ded9d..e5e3e44 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -16,6 +16,7 @@
 
 package android.test.mock;
 
+import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -317,6 +318,13 @@
     }
 
     /** @hide */
+    @SystemApi
+    @Override
+    public void sendBroadcast(Intent intent, String receiverPermission, Bundle options) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
     @Override
     public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
         throw new UnsupportedOperationException();
@@ -336,6 +344,15 @@
     }
 
     /** @hide */
+    @SystemApi
+    @Override
+    public void sendOrderedBroadcast(Intent intent, String receiverPermission,
+            Bundle options, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData,
+            Bundle initialExtras) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
     @Override
     public void sendOrderedBroadcast(Intent intent, String receiverPermission, int appOp,
             BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 6be5a95..422b2aa 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1506,6 +1506,12 @@
     }
 
     @Override
+    public void sendBroadcast(Intent arg0, String arg1, Bundle arg2) {
+        // pass
+
+    }
+
+    @Override
     public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
         // pass
     }
@@ -1525,6 +1531,14 @@
     }
 
     @Override
+    public void sendOrderedBroadcast(Intent arg0, String arg1,
+            Bundle arg7, BroadcastReceiver arg2, Handler arg3, int arg4, String arg5,
+            Bundle arg6) {
+        // pass
+
+    }
+
+    @Override
     public void sendOrderedBroadcast(Intent intent, String receiverPermission, int appOp,
             BroadcastReceiver resultReceiver, Handler scheduler, int initialCode,
             String initialData, Bundle initialExtras) {