Merge "Shrink default path cache size" into mnc-dev
diff --git a/Android.mk b/Android.mk
index 27dedd9..264ad48 100644
--- a/Android.mk
+++ b/Android.mk
@@ -483,6 +483,7 @@
 	frameworks/base/graphics/java/android/graphics/Rect.aidl \
 	frameworks/base/core/java/android/accounts/AuthenticatorDescription.aidl \
 	frameworks/base/core/java/android/accounts/Account.aidl \
+	frameworks/base/core/java/android/app/admin/SystemUpdatePolicy.aidl \
 	frameworks/base/core/java/android/print/PrintDocumentInfo.aidl \
 	frameworks/base/core/java/android/print/PageRange.aidl \
 	frameworks/base/core/java/android/print/PrintAttributes.aidl \
diff --git a/api/current.txt b/api/current.txt
index f57e1f6..630b84f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3761,7 +3761,9 @@
     method public android.app.AlarmManager.AlarmClockInfo getNextAlarmClock();
     method public void set(int, long, android.app.PendingIntent);
     method public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent);
+    method public void setAndAllowWhileIdle(int, long, android.app.PendingIntent);
     method public void setExact(int, long, android.app.PendingIntent);
+    method public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent);
     method public void setInexactRepeating(int, long, long, android.app.PendingIntent);
     method public void setRepeating(int, long, long, android.app.PendingIntent);
     method public void setTime(long);
@@ -5863,23 +5865,21 @@
     field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
   }
 
-  public class SystemUpdatePolicy {
-    ctor public SystemUpdatePolicy();
+  public class SystemUpdatePolicy implements android.os.Parcelable {
+    method public static android.app.admin.SystemUpdatePolicy createAutomaticInstallPolicy();
+    method public static android.app.admin.SystemUpdatePolicy createPostponeInstallPolicy();
+    method public static android.app.admin.SystemUpdatePolicy createWindowedInstallPolicy(int, int);
+    method public int describeContents();
     method public int getInstallWindowEnd();
     method public int getInstallWindowStart();
     method public int getPolicyType();
-    method public void setAutomaticInstallPolicy();
-    method public void setPostponeInstallPolicy();
-    method public void setWindowedInstallPolicy(int, int) throws android.app.admin.SystemUpdatePolicy.InvalidWindowException;
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.admin.SystemUpdatePolicy> CREATOR;
     field public static final int TYPE_INSTALL_AUTOMATIC = 1; // 0x1
     field public static final int TYPE_INSTALL_WINDOWED = 2; // 0x2
     field public static final int TYPE_POSTPONE = 3; // 0x3
   }
 
-  public static class SystemUpdatePolicy.InvalidWindowException extends java.lang.Exception {
-    ctor public SystemUpdatePolicy.InvalidWindowException(java.lang.String);
-  }
-
 }
 
 package android.app.backup {
@@ -8251,7 +8251,6 @@
     field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
     field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
     field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
-    field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
     field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
@@ -17827,6 +17826,7 @@
 
   public static abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
     ctor public TvInputService.Session(android.content.Context);
+    method public void layoutSurface(int, int, int, int);
     method public void notifyChannelRetuned(android.net.Uri);
     method public void notifyContentAllowed();
     method public void notifyContentBlocked(android.media.tv.TvContentRating);
@@ -17841,6 +17841,7 @@
     method public boolean onKeyLongPress(int, android.view.KeyEvent);
     method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
     method public boolean onKeyUp(int, android.view.KeyEvent);
+    method public void onOverlayViewSizeChanged(int, int);
     method public abstract void onRelease();
     method public boolean onSelectTrack(int, java.lang.String);
     method public abstract void onSetCaptionEnabled(boolean);
@@ -18092,7 +18093,7 @@
     method public void ignoreNetworkWithCaptivePortal(android.net.Network, java.lang.String);
     method public boolean isActiveNetworkMetered();
     method public boolean isDefaultNetworkActive();
-    method public static boolean isNetworkTypeValid(int);
+    method public static deprecated boolean isNetworkTypeValid(int);
     method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void releaseNetworkRequest(android.app.PendingIntent);
     method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
@@ -27231,7 +27232,6 @@
     method public void copyTo(short[]);
     method public void copyTo(int[]);
     method public void copyTo(float[]);
-    method public void copyToFieldPacker(int, int, int, int, android.renderscript.FieldPacker);
     method public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int);
     method public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap);
     method public static android.renderscript.Allocation createCubemapFromCubeFaces(android.renderscript.RenderScript, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int);
@@ -28684,10 +28684,8 @@
   public final class ChooserTarget implements android.os.Parcelable {
     ctor public ChooserTarget(java.lang.CharSequence, android.graphics.Bitmap, float, android.app.PendingIntent);
     ctor public ChooserTarget(java.lang.CharSequence, android.graphics.Bitmap, float, android.content.IntentSender);
-    ctor public ChooserTarget(java.lang.CharSequence, android.graphics.Bitmap, float, android.content.Intent);
     method public int describeContents();
     method public android.graphics.Bitmap getIcon();
-    method public android.content.Intent getIntent();
     method public android.content.IntentSender getIntentSender();
     method public float getScore();
     method public java.lang.CharSequence getTitle();
diff --git a/api/system-current.txt b/api/system-current.txt
index 79908fa..e606a00 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3851,7 +3851,9 @@
     method public void set(int, long, android.app.PendingIntent);
     method public void set(int, long, long, long, android.app.PendingIntent, android.os.WorkSource);
     method public void setAlarmClock(android.app.AlarmManager.AlarmClockInfo, android.app.PendingIntent);
+    method public void setAndAllowWhileIdle(int, long, android.app.PendingIntent);
     method public void setExact(int, long, android.app.PendingIntent);
+    method public void setExactAndAllowWhileIdle(int, long, android.app.PendingIntent);
     method public void setInexactRepeating(int, long, long, android.app.PendingIntent);
     method public void setRepeating(int, long, long, android.app.PendingIntent);
     method public void setTime(long);
@@ -5974,23 +5976,21 @@
     field public static final int WIPE_RESET_PROTECTION_DATA = 2; // 0x2
   }
 
-  public class SystemUpdatePolicy {
-    ctor public SystemUpdatePolicy();
+  public class SystemUpdatePolicy implements android.os.Parcelable {
+    method public static android.app.admin.SystemUpdatePolicy createAutomaticInstallPolicy();
+    method public static android.app.admin.SystemUpdatePolicy createPostponeInstallPolicy();
+    method public static android.app.admin.SystemUpdatePolicy createWindowedInstallPolicy(int, int);
+    method public int describeContents();
     method public int getInstallWindowEnd();
     method public int getInstallWindowStart();
     method public int getPolicyType();
-    method public void setAutomaticInstallPolicy();
-    method public void setPostponeInstallPolicy();
-    method public void setWindowedInstallPolicy(int, int) throws android.app.admin.SystemUpdatePolicy.InvalidWindowException;
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.admin.SystemUpdatePolicy> CREATOR;
     field public static final int TYPE_INSTALL_AUTOMATIC = 1; // 0x1
     field public static final int TYPE_INSTALL_WINDOWED = 2; // 0x2
     field public static final int TYPE_POSTPONE = 3; // 0x3
   }
 
-  public static class SystemUpdatePolicy.InvalidWindowException extends java.lang.Exception {
-    ctor public SystemUpdatePolicy.InvalidWindowException(java.lang.String);
-  }
-
 }
 
 package android.app.backup {
@@ -8477,7 +8477,6 @@
     field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
     field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
     field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
-    field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
     field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
@@ -9505,6 +9504,7 @@
     method public abstract android.content.pm.PackageInstaller getPackageInstaller();
     method public abstract java.lang.String[] getPackagesForUid(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
+    method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.PermissionInfo getPermissionInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
@@ -9522,7 +9522,7 @@
     method public abstract android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle);
     method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public abstract void grantPermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract boolean hasSystemFeature(java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9538,10 +9538,11 @@
     method public abstract android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int);
     method public abstract android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int);
     method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
-    method public abstract void revokePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
+    method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public abstract void verifyPendingInstall(int, int);
     field public static final java.lang.String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS";
     field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
@@ -9627,6 +9628,10 @@
     field public static final java.lang.String FEATURE_WEBVIEW = "android.software.webview";
     field public static final java.lang.String FEATURE_WIFI = "android.hardware.wifi";
     field public static final java.lang.String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
+    field public static final int FLAG_PERMISSION_POLICY_FIXED = 4; // 0x4
+    field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
+    field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2
+    field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1
     field public static final int GET_ACTIVITIES = 1; // 0x1
     field public static final int GET_CONFIGURATIONS = 16384; // 0x4000
     field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200
@@ -9679,6 +9684,7 @@
     field public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103; // 0xffffff99
     field public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102; // 0xffffff9a
     field public static final int INSTALL_SUCCEEDED = 1; // 0x1
+    field public static final int MASK_PERMISSION_FLAGS = 15; // 0xf
     field public static final int MATCH_ALL = 131072; // 0x20000
     field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
     field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
@@ -9699,6 +9705,9 @@
     ctor public PackageManager.NameNotFoundException(java.lang.String);
   }
 
+  public static abstract class PackageManager.PermissionFlags implements java.lang.annotation.Annotation {
+  }
+
   public class PackageStats implements android.os.Parcelable {
     ctor public PackageStats(java.lang.String);
     ctor public PackageStats(android.os.Parcel);
@@ -19364,7 +19373,7 @@
     method public java.util.List<android.media.tv.TvTrackInfo> getTracks(int);
     method protected void onLayout(boolean, int, int, int, int);
     method public boolean onUnhandledInputEvent(android.view.InputEvent);
-    method public void requestUnblockContent(android.media.tv.TvContentRating);
+    method public deprecated void requestUnblockContent(android.media.tv.TvContentRating);
     method public void reset();
     method public void selectTrack(int, java.lang.String);
     method public void sendAppPrivateCommand(java.lang.String, android.os.Bundle);
@@ -19382,6 +19391,7 @@
     method public void timeShiftSetPlaybackRate(float, int);
     method public void tune(java.lang.String, android.net.Uri);
     method public void tune(java.lang.String, android.net.Uri, android.os.Bundle);
+    method public void unblockContent(android.media.tv.TvContentRating);
   }
 
   public static abstract interface TvView.OnUnhandledInputEventListener {
@@ -19560,7 +19570,7 @@
     method public void ignoreNetworkWithCaptivePortal(android.net.Network, java.lang.String);
     method public boolean isActiveNetworkMetered();
     method public boolean isDefaultNetworkActive();
-    method public static boolean isNetworkTypeValid(int);
+    method public static deprecated boolean isNetworkTypeValid(int);
     method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void releaseNetworkRequest(android.app.PendingIntent);
     method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
@@ -29245,7 +29255,6 @@
     method public void copyTo(short[]);
     method public void copyTo(int[]);
     method public void copyTo(float[]);
-    method public void copyToFieldPacker(int, int, int, int, android.renderscript.FieldPacker);
     method public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int);
     method public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap);
     method public static android.renderscript.Allocation createCubemapFromCubeFaces(android.renderscript.RenderScript, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int);
@@ -30698,10 +30707,8 @@
   public final class ChooserTarget implements android.os.Parcelable {
     ctor public ChooserTarget(java.lang.CharSequence, android.graphics.Bitmap, float, android.app.PendingIntent);
     ctor public ChooserTarget(java.lang.CharSequence, android.graphics.Bitmap, float, android.content.IntentSender);
-    ctor public ChooserTarget(java.lang.CharSequence, android.graphics.Bitmap, float, android.content.Intent);
     method public int describeContents();
     method public android.graphics.Bitmap getIcon();
-    method public android.content.Intent getIntent();
     method public android.content.IntentSender getIntentSender();
     method public float getScore();
     method public java.lang.CharSequence getTitle();
@@ -34042,6 +34049,7 @@
     method public android.content.pm.PackageInstaller getPackageInstaller();
     method public java.lang.String[] getPackagesForUid(int);
     method public java.util.List<android.content.pm.PackageInfo> getPackagesHoldingPermissions(java.lang.String[], int);
+    method public int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
     method public android.content.pm.PermissionGroupInfo getPermissionGroupInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PermissionInfo getPermissionInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public int getPreferredActivities(java.util.List<android.content.IntentFilter>, java.util.List<android.content.ComponentName>, java.lang.String);
@@ -34059,7 +34067,7 @@
     method public android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle);
     method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
-    method public void grantPermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public boolean hasSystemFeature(java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -34075,11 +34083,12 @@
     method public android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int);
     method public android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int);
     method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
-    method public void revokePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public boolean setDefaultBrowserPackageName(java.lang.String, int);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
+    method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public void verifyPendingInstall(int, int);
   }
 
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 39de1dc7..eb834f2 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1609,9 +1609,9 @@
 
         try {
             if (grant) {
-                mPm.grantPermission(pkg, perm, userId);
+                mPm.grantRuntimePermission(pkg, perm, userId);
             } else {
-                mPm.revokePermission(pkg, perm, userId);
+                mPm.revokeRuntimePermission(pkg, perm, userId);
             }
             return 0;
         } catch (RemoteException e) {
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 6503d89..951fe49 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -16,9 +16,10 @@
 
 package android.animation;
 
+import android.util.ArrayMap;
+
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -68,7 +69,7 @@
      * to a single node representing that Animator, not create a new Node
      * if one already exists.
      */
-    private HashMap<Animator, Node> mNodeMap = new HashMap<Animator, Node>();
+    private ArrayMap<Animator, Node> mNodeMap = new ArrayMap<Animator, Node>();
 
     /**
      * Set of all nodes created for this AnimatorSet. This list is used upon
@@ -646,7 +647,7 @@
         anim.mTerminated = false;
         anim.mStarted = false;
         anim.mPlayingSet = new ArrayList<Animator>();
-        anim.mNodeMap = new HashMap<Animator, Node>();
+        anim.mNodeMap = new ArrayMap<Animator, Node>();
         anim.mNodes = new ArrayList<Node>(nodeCount);
         anim.mSortedNodes = new ArrayList<Node>(nodeCount);
         anim.mReversible = mReversible;
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index b0fda9c..5e7bd0d 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -71,10 +71,7 @@
  * {@link android.content.Context#getSystemService
  * Context.getSystemService(Context.ALARM_SERVICE)}.
  */
-public class AlarmManager
-{
-    private static final String TAG = "AlarmManager";
-
+public class AlarmManager {
     /**
      * Alarm time in {@link System#currentTimeMillis System.currentTimeMillis()}
      * (wall clock time in UTC), which will wake up the device when
@@ -558,7 +555,93 @@
             long intervalMillis, PendingIntent operation) {
         setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, 0, operation, null, null);
     }
-    
+
+    /**
+     * Like {@link #set(int, long, PendingIntent)}, but this alarm will be allowed to execute
+     * even when the system is in low-power idle modes.  This type of alarm must <b>only</b>
+     * be used for situations where it is actually required that the alarm go off while in
+     * idle -- a reasonable example would be for a calendar notification that should make a
+     * sound so the user is aware of it.  These alarms can significantly impact the power use
+     * of the device when idle (and thus cause significant battery blame to the app scheduling
+     * them), so they should be used with care.
+     *
+     * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen
+     * out of order with any other alarms, even those from the same app.  This will clearly happen
+     * when the device is idle (since this alarm can go off while idle, when any other alarms
+     * from the app will be held until later), but may also happen even when not idle.</p>
+     *
+     * <p>Regardless of the app's target SDK version, this call always allows batching of the
+     * alarm.</p>
+     *
+     * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP},
+     *        {@link #RTC}, or {@link #RTC_WAKEUP}.
+     * @param triggerAtMillis time in milliseconds that the alarm should go
+     * off, using the appropriate clock (depending on the alarm type).
+     * @param operation Action to perform when the alarm goes off;
+     * typically comes from {@link PendingIntent#getBroadcast
+     * IntentSender.getBroadcast()}.
+     *
+     * @see #set(int, long, PendingIntent)
+     * @see #setExactAndAllowWhileIdle
+     * @see #cancel
+     * @see android.content.Context#sendBroadcast
+     * @see android.content.Context#registerReceiver
+     * @see android.content.Intent#filterEquals
+     * @see #ELAPSED_REALTIME
+     * @see #ELAPSED_REALTIME_WAKEUP
+     * @see #RTC
+     * @see #RTC_WAKEUP
+     */
+    public void setAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) {
+        setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, 0, FLAG_ALLOW_WHILE_IDLE, operation,
+                null, null);
+    }
+
+    /**
+     * Like {@link #setExact(int, long, PendingIntent)}, but this alarm will be allowed to execute
+     * even when the system is in low-power idle modes.  If you don't need exact scheduling of
+     * the alarm but still need to execute while idle, consider using
+     * {@link #setAndAllowWhileIdle}.  This type of alarm must <b>only</b>
+     * be used for situations where it is actually required that the alarm go off while in
+     * idle -- a reasonable example would be for a calendar notification that should make a
+     * sound so the user is aware of it.  These alarms can significantly impact the power use
+     * of the device when idle (and thus cause significant battery blame to the app scheduling
+     * them), so they should be used with care.
+     *
+     * <p>Unlike other alarms, the system is free to reschedule this type of alarm to happen
+     * out of order with any other alarms, even those from the same app.  This will clearly happen
+     * when the device is idle (since this alarm can go off while idle, when any other alarms
+     * from the app will be held until later), but may also happen even when not idle.
+     * Note that the OS will allow itself more flexibility for scheduling these alarms than
+     * regular exact alarms, since the application has opted into this behavior.  When the
+     * device is idle it may take even more liberties with scheduling in order to optimize
+     * for battery life.</p>
+     *
+     * @param type One of {@link #ELAPSED_REALTIME}, {@link #ELAPSED_REALTIME_WAKEUP},
+     *        {@link #RTC}, or {@link #RTC_WAKEUP}.
+     * @param triggerAtMillis time in milliseconds that the alarm should go
+     *        off, using the appropriate clock (depending on the alarm type).
+     * @param operation Action to perform when the alarm goes off;
+     *        typically comes from {@link PendingIntent#getBroadcast
+     *        IntentSender.getBroadcast()}.
+     *
+     * @see #set
+     * @see #setRepeating
+     * @see #setWindow
+     * @see #cancel
+     * @see android.content.Context#sendBroadcast
+     * @see android.content.Context#registerReceiver
+     * @see android.content.Intent#filterEquals
+     * @see #ELAPSED_REALTIME
+     * @see #ELAPSED_REALTIME_WAKEUP
+     * @see #RTC
+     * @see #RTC_WAKEUP
+     */
+    public void setExactAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation) {
+        setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_ALLOW_WHILE_IDLE, operation,
+                null, null);
+    }
+
     /**
      * Remove any alarms with a matching {@link Intent}.
      * Any alarm, of any type, whose Intent matches this one (as defined by
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 07eee12..04f6430 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -446,18 +446,40 @@
     }
 
     @Override
-    public void grantPermission(String packageName, String permissionName, UserHandle user) {
+    public void grantRuntimePermission(String packageName, String permissionName,
+            UserHandle user) {
         try {
-            mPM.grantPermission(packageName, permissionName, user.getIdentifier());
+            mPM.grantRuntimePermission(packageName, permissionName, user.getIdentifier());
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
     }
 
     @Override
-    public void revokePermission(String packageName, String permissionName, UserHandle user) {
+    public void revokeRuntimePermission(String packageName, String permissionName,
+            UserHandle user) {
         try {
-            mPM.revokePermission(packageName, permissionName, user.getIdentifier());
+            mPM.revokeRuntimePermission(packageName, permissionName, user.getIdentifier());
+        } catch (RemoteException e) {
+            throw new RuntimeException("Package manager has died", e);
+        }
+    }
+
+    @Override
+    public int getPermissionFlags(String permissionName, String packageName, UserHandle user) {
+        try {
+            return mPM.getPermissionFlags(permissionName, packageName, user.getIdentifier());
+        } catch (RemoteException e) {
+            throw new RuntimeException("Package manager has died", e);
+        }
+    }
+
+    @Override
+    public void updatePermissionFlags(String permissionName, String packageName,
+            int flagMask, int flagValues, UserHandle user) {
+        try {
+            mPM.updatePermissionFlags(permissionName, packageName, flagMask,
+                    flagValues, user.getIdentifier());
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ae07206..3fb7059 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4253,11 +4253,7 @@
     public void setSystemUpdatePolicy(ComponentName who, SystemUpdatePolicy policy) {
         if (mService != null) {
             try {
-                if (policy != null) {
-                    mService.setSystemUpdatePolicy(who, policy.getPolicyBundle());
-                } else {
-                    mService.setSystemUpdatePolicy(who, null);
-                }
+                mService.setSystemUpdatePolicy(who, policy);
             } catch (RemoteException re) {
                 Log.w(TAG, "Error calling setSystemUpdatePolicy", re);
             }
@@ -4272,12 +4268,7 @@
     public SystemUpdatePolicy getSystemUpdatePolicy() {
         if (mService != null) {
             try {
-                PersistableBundle bundle = mService.getSystemUpdatePolicy();
-                if (bundle != null) {
-                    return new SystemUpdatePolicy(bundle);
-                } else {
-                    return null;
-                }
+                return mService.getSystemUpdatePolicy();
             } catch (RemoteException re) {
                 Log.w(TAG, "Error calling getSystemUpdatePolicy", re);
             }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index e81e7c1..481ff62 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -17,6 +17,7 @@
 
 package android.app.admin;
 
+import android.app.admin.SystemUpdatePolicy;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -221,8 +222,8 @@
     void setUserIcon(in ComponentName admin, in Bitmap icon);
 
     void sendDeviceInitializerStatus(int statusCode, String description);
-    void setSystemUpdatePolicy(in ComponentName who, in PersistableBundle policy);
-    PersistableBundle getSystemUpdatePolicy();
+    void setSystemUpdatePolicy(in ComponentName who, in SystemUpdatePolicy policy);
+    SystemUpdatePolicy getSystemUpdatePolicy();
 
     boolean setKeyguardDisabled(in ComponentName admin, boolean disabled);
     boolean setStatusBarDisabled(in ComponentName who, boolean disabled);
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.aidl b/core/java/android/app/admin/SystemUpdatePolicy.aidl
new file mode 100644
index 0000000..58e8d15
--- /dev/null
+++ b/core/java/android/app/admin/SystemUpdatePolicy.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 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.admin;
+
+parcelable SystemUpdatePolicy;
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java
index de56cd0..20ddb77 100644
--- a/core/java/android/app/admin/SystemUpdatePolicy.java
+++ b/core/java/android/app/admin/SystemUpdatePolicy.java
@@ -17,8 +17,15 @@
 package android.app.admin;
 
 import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.PersistableBundle;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -28,7 +35,7 @@
  * @see DevicePolicyManager#setSystemUpdatePolicy
  * @see DevicePolicyManager#getSystemUpdatePolicy
  */
-public class SystemUpdatePolicy {
+public class SystemUpdatePolicy implements Parcelable {
 
     /** @hide */
     @IntDef({
@@ -39,6 +46,10 @@
     @interface SystemUpdatePolicyType {}
 
     /**
+     * Unknown policy type, used only internally.
+     */
+    private static final int TYPE_UNKNOWN = -1;
+    /**
      * Install system update automatically as soon as one is available.
      */
     public static final int TYPE_INSTALL_AUTOMATIC = 1;
@@ -63,45 +74,40 @@
     private static final String KEY_POLICY_TYPE = "policy_type";
     private static final String KEY_INSTALL_WINDOW_START = "install_window_start";
     private static final String KEY_INSTALL_WINDOW_END = "install_window_end";
-
-    private PersistableBundle mPolicy;
-
-    public  SystemUpdatePolicy() {
-        mPolicy = new PersistableBundle();
-    }
-
     /**
-     * Construct an SystemUpdatePolicy object from a bundle.
-     * @hide
+     * The upper boundary of the daily maintenance window: 24 * 60 minutes.
      */
-    public SystemUpdatePolicy(PersistableBundle in) {
-        mPolicy = new PersistableBundle(in);
+    private static final int WINDOW_BOUNDARY = 24 * 60;
+
+    @SystemUpdatePolicyType
+    private int mPolicyType;
+
+    private int mMaintenanceWindowStart;
+    private int mMaintenanceWindowEnd;
+
+
+    private SystemUpdatePolicy() {
+        mPolicyType = TYPE_UNKNOWN;
     }
 
     /**
-     * Retrieve the underlying bundle where the policy is stored.
-     * @hide
-     */
-    public PersistableBundle getPolicyBundle() {
-        return new PersistableBundle(mPolicy);
-    }
-
-    /**
-     * Set the policy to: install update automatically as soon as one is available.
+     * Create a policy object and set it to install update automatically as soon as one is
+     * available.
      *
      * @see #TYPE_INSTALL_AUTOMATIC
      */
-    public void setAutomaticInstallPolicy() {
-        mPolicy.clear();
-        mPolicy.putInt(KEY_POLICY_TYPE, TYPE_INSTALL_AUTOMATIC);
+    public static SystemUpdatePolicy createAutomaticInstallPolicy() {
+        SystemUpdatePolicy policy = new SystemUpdatePolicy();
+        policy.mPolicyType = TYPE_INSTALL_AUTOMATIC;
+        return policy;
     }
 
     /**
-     * Set the policy to: new system update will only be installed automatically when the system
-     * clock is inside a daily maintenance window. If the start and end times are the same, the
-     * window is considered to include the WHOLE 24 hours, that is, updates can install at any time.
-     * If the given window in invalid, a {@link SystemUpdatePolicy.InvalidWindowException} will be
-     * thrown. If start time is later than end time, the window is considered spanning midnight,
+     * Create a policy object and set it to: new system update will only be installed automatically
+     * when the system clock is inside a daily maintenance window. If the start and end times are
+     * the same, the window is considered to include the WHOLE 24 hours, that is, updates can
+     * install at any time. If the given window in invalid, a {@link IllegalArgumentException} will
+     * be thrown. If start time is later than end time, the window is considered spanning midnight,
      * i.e. end time donates a time on the next day. The maintenance window will last for 30 days,
      * after which the system should revert back to its normal behavior as if no policy were set.
      *
@@ -111,25 +117,29 @@
      *            midnight in the device's local time. Must be in the range of [0, 1440).
      * @see #TYPE_INSTALL_WINDOWED
      */
-    public void setWindowedInstallPolicy(int startTime, int endTime) throws InvalidWindowException{
-        if (startTime < 0 || startTime >= 1440 || endTime < 0 || endTime >= 1440) {
-            throw new InvalidWindowException("startTime and endTime must be inside [0, 1440)");
+    public static SystemUpdatePolicy createWindowedInstallPolicy(int startTime, int endTime) {
+        if (startTime < 0 || startTime >= WINDOW_BOUNDARY
+                || endTime < 0 || endTime >= WINDOW_BOUNDARY) {
+            throw new IllegalArgumentException("startTime and endTime must be inside [0, 1440)");
         }
-        mPolicy.clear();
-        mPolicy.putInt(KEY_POLICY_TYPE, TYPE_INSTALL_WINDOWED);
-        mPolicy.putInt(KEY_INSTALL_WINDOW_START, startTime);
-        mPolicy.putInt(KEY_INSTALL_WINDOW_END, endTime);
+        SystemUpdatePolicy policy = new SystemUpdatePolicy();
+        policy.mPolicyType = TYPE_INSTALL_WINDOWED;
+        policy.mMaintenanceWindowStart = startTime;
+        policy.mMaintenanceWindowEnd = endTime;
+        return policy;
     }
 
     /**
-     * Set the policy to: block installation for a maximum period of 30 days. After expiration the
-     * system should revert back to its normal behavior as if no policy were set.
+     * Create a policy object and set it to block installation for a maximum period of 30 days.
+     * After expiration the system should revert back to its normal behavior as if no policy were
+     * set.
      *
      * @see #TYPE_POSTPONE
      */
-    public void setPostponeInstallPolicy() {
-        mPolicy.clear();
-        mPolicy.putInt(KEY_POLICY_TYPE, TYPE_POSTPONE);
+    public static SystemUpdatePolicy createPostponeInstallPolicy() {
+        SystemUpdatePolicy policy = new SystemUpdatePolicy();
+        policy.mPolicyType = TYPE_POSTPONE;
+        return policy;
     }
 
     /**
@@ -140,7 +150,7 @@
      */
     @SystemUpdatePolicyType
     public int getPolicyType() {
-        return mPolicy.getInt(KEY_POLICY_TYPE, -1);
+        return mPolicyType;
     }
 
     /**
@@ -150,8 +160,8 @@
      * or -1 if the policy does not have a maintenance window.
      */
     public int getInstallWindowStart() {
-        if (getPolicyType() == TYPE_INSTALL_WINDOWED) {
-            return mPolicy.getInt(KEY_INSTALL_WINDOW_START, -1);
+        if (mPolicyType == TYPE_INSTALL_WINDOWED) {
+            return mMaintenanceWindowStart;
         } else {
             return -1;
         }
@@ -164,26 +174,98 @@
      * or -1 if the policy does not have a maintenance window.
      */
     public int getInstallWindowEnd() {
-        if (getPolicyType() == TYPE_INSTALL_WINDOWED) {
-            return mPolicy.getInt(KEY_INSTALL_WINDOW_END, -1);
+        if (mPolicyType == TYPE_INSTALL_WINDOWED) {
+            return mMaintenanceWindowEnd;
         } else {
             return -1;
         }
     }
 
+    /**
+     * Return if this object represents a valid policy.
+     * @hide
+     */
+    public boolean isValid() {
+        if (mPolicyType == TYPE_INSTALL_AUTOMATIC || mPolicyType == TYPE_POSTPONE) {
+            return true;
+        } else if (mPolicyType == TYPE_INSTALL_WINDOWED) {
+            return mMaintenanceWindowStart >= 0 && mMaintenanceWindowStart < WINDOW_BOUNDARY
+                    && mMaintenanceWindowEnd >= 0 && mMaintenanceWindowEnd < WINDOW_BOUNDARY;
+        } else {
+            return false;
+        }
+    }
+
     @Override
     public String toString() {
-        return mPolicy.toString();
+        return String.format("SystemUpdatePolicy (type: %d, windowStart: %d, windowEnd: %d)",
+                mPolicyType, mMaintenanceWindowStart, mMaintenanceWindowEnd);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mPolicyType);
+        dest.writeInt(mMaintenanceWindowStart);
+        dest.writeInt(mMaintenanceWindowEnd);
+    }
+
+    public static final Parcelable.Creator<SystemUpdatePolicy> CREATOR =
+            new Parcelable.Creator<SystemUpdatePolicy>() {
+
+                @Override
+                public SystemUpdatePolicy createFromParcel(Parcel source) {
+                    SystemUpdatePolicy policy = new SystemUpdatePolicy();
+                    policy.mPolicyType = source.readInt();
+                    policy.mMaintenanceWindowStart = source.readInt();
+                    policy.mMaintenanceWindowEnd = source.readInt();
+                    return policy;
+                }
+
+                @Override
+                public SystemUpdatePolicy[] newArray(int size) {
+                    return new SystemUpdatePolicy[size];
+                }
+    };
+
+
+    /**
+     * @hide
+     */
+    public static SystemUpdatePolicy restoreFromXml(XmlPullParser parser) {
+        try {
+            SystemUpdatePolicy policy = new SystemUpdatePolicy();
+            String value = parser.getAttributeValue(null, KEY_POLICY_TYPE);
+            if (value != null) {
+                policy.mPolicyType = Integer.parseInt(value);
+
+                value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_START);
+                if (value != null) {
+                    policy.mMaintenanceWindowStart = Integer.parseInt(value);
+                }
+                value = parser.getAttributeValue(null, KEY_INSTALL_WINDOW_END);
+                if (value != null) {
+                    policy.mMaintenanceWindowEnd = Integer.parseInt(value);
+                }
+                return policy;
+            }
+        } catch (NumberFormatException e) {
+            // Fail through
+        }
+        return null;
     }
 
     /**
-     * Exception thrown by {@link SystemUpdatePolicy#setWindowedInstallPolicy(int, int)} in case the
-     * specified window is invalid.
+     * @hide
      */
-    public static class InvalidWindowException extends Exception {
-        public InvalidWindowException(String reason) {
-            super(reason);
-        }
+    public void saveToXml(XmlSerializer out) throws IOException {
+        out.attribute(null, KEY_POLICY_TYPE, Integer.toString(mPolicyType));
+        out.attribute(null, KEY_INSTALL_WINDOW_START, Integer.toString(mMaintenanceWindowStart));
+        out.attribute(null, KEY_INSTALL_WINDOW_END, Integer.toString(mMaintenanceWindowEnd));
     }
 }
 
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index abfc435..81c7422 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -46,6 +46,13 @@
     public long mLastTimeUsed;
 
     /**
+     * Last time the package was used and the beginning of the idle countdown.
+     * This uses a different timebase that is about how much the device has been in use in general.
+     * {@hide}
+     */
+    public long mBeginIdleTime;
+
+    /**
      * {@hide}
      */
     public long mTotalTimeInForeground;
@@ -74,6 +81,7 @@
         mTotalTimeInForeground = stats.mTotalTimeInForeground;
         mLaunchCount = stats.mLaunchCount;
         mLastEvent = stats.mLastEvent;
+        mBeginIdleTime = stats.mBeginIdleTime;
     }
 
     public String getPackageName() {
@@ -110,6 +118,15 @@
     }
 
     /**
+     * @hide
+     * Get the last time this package was active, measured in milliseconds. This timestamp
+     * uses a timebase that represents how much the device was used and not wallclock time.
+     */
+    public long getBeginIdleTime() {
+        return mBeginIdleTime;
+    }
+
+    /**
      * Get the total time this package spent in the foreground, measured in milliseconds.
      */
     public long getTotalTimeInForeground() {
@@ -133,6 +150,7 @@
             mLastEvent = right.mLastEvent;
             mEndTimeStamp = right.mEndTimeStamp;
             mLastTimeUsed = right.mLastTimeUsed;
+            mBeginIdleTime = right.mBeginIdleTime;
         }
         mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
         mTotalTimeInForeground += right.mTotalTimeInForeground;
@@ -153,6 +171,7 @@
         dest.writeLong(mTotalTimeInForeground);
         dest.writeInt(mLaunchCount);
         dest.writeInt(mLastEvent);
+        dest.writeLong(mBeginIdleTime);
     }
 
     public static final Creator<UsageStats> CREATOR = new Creator<UsageStats>() {
@@ -166,6 +185,7 @@
             stats.mTotalTimeInForeground = in.readLong();
             stats.mLaunchCount = in.readInt();
             stats.mLastEvent = in.readInt();
+            stats.mBeginIdleTime = in.readLong();
             return stats;
         }
 
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 8b3fc2e..7bcc038 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -69,14 +69,6 @@
     public abstract boolean isAppIdle(String packageName, int userId);
 
     /**
-     * Returns the most recent time that the specified package was active for the given user.
-     * @param packageName The package to search.
-     * @param userId The user id of the user of interest.
-     * @return The timestamp of when the package was last used, or -1 if it hasn't been used.
-     */
-    public abstract long getLastPackageAccessTime(String packageName, int userId);
-
-    /**
      * Sets up a listener for changes to packages being accessed.
      * @param listener A listener within the system process.
      */
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index a3eceb5..7a894ae 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -101,8 +101,8 @@
     void getActivityEnergyInfoFromController();
     BluetoothActivityEnergyInfo reportActivityInfo();
 
-    // for dumpsys support
-    String dump();
+    // For dumpsys support
+    void dump(in ParcelFileDescriptor fd);
     void onLeServiceUp();
     void onBrEdrDown();
 }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6f543a8..7d76760 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3365,14 +3365,6 @@
     public static final String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
 
     /**
-     * A Parcelable[] of {@link android.service.chooser.ChooserTarget ChooserTarget} objects
-     * as set with {@link #putExtra(String, Parcelable[])} representing additional app-specific
-     * targets to place at the front of the list of choices. Shown to the user with
-     * {@link #ACTION_CHOOSER}.
-     */
-    public static final String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
-
-    /**
      * A Bundle forming a mapping of potential target package names to different extras Bundles
      * to add to the default intent extras in {@link #EXTRA_INTENT} when used with
      * {@link #ACTION_CHOOSER}. Each key should be a package name. The package need not
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 94b0223..ddff782 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -96,9 +96,14 @@
 
     void removePermission(String name);
 
-    void grantPermission(String packageName, String permissionName, int userId);
+    void grantRuntimePermission(String packageName, String permissionName, int userId);
 
-    void revokePermission(String packageName, String permissionName, int userId);
+    void revokeRuntimePermission(String packageName, String permissionName, int userId);
+
+    int getPermissionFlags(String permissionName, String packageName, int userId);
+
+    void updatePermissionFlags(String permissionName, String packageName, int flagMask,
+            int flagValues, int userId);
 
     boolean isProtectedBroadcast(String actionName);
 
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index 87b97aa..6827d7a 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -103,20 +103,11 @@
      * density DPI values from {@link DisplayMetrics}.
      * @see #getBadgedIcon(int)
      * @see DisplayMetrics
-     * @return The drawable associated with the activity
+     * @return The drawable associated with the activity.
      */
     public Drawable getIcon(int density) {
-        int iconRes = mResolveInfo.getIconResource();
-        Resources resources = null;
-        Drawable icon = null;
-        // Get the preferred density icon from the app's resources
-        if (density != 0 && iconRes != 0) {
-            try {
-                resources = mPm.getResourcesForApplication(mActivityInfo.applicationInfo);
-                icon = resources.getDrawableForDensity(iconRes, density);
-            } catch (NameNotFoundException | Resources.NotFoundException exc) {
-            }
-        }
+        final int iconRes = mResolveInfo.getIconResource();
+        Drawable icon = getDrawableForDensity(iconRes, density);
         // Get the default density icon
         if (icon == null) {
             icon = mResolveInfo.loadIcon(mPm);
@@ -125,6 +116,46 @@
     }
 
     /**
+     * Returns the icon for this activity, without any badging for the profile.
+     * This function can get the icon no matter the icon needs to be badged or not.
+     * @param density The preferred density of the icon, zero for default density. Use
+     * density DPI values from {@link DisplayMetrics}.
+     * @see #getBadgedIcon(int)
+     * @see DisplayMetrics
+     * @return The drawable associated with the activity.
+     */
+    private Drawable getOriginalIcon(int density) {
+        final int iconRes = mResolveInfo.getIconResourceInternal();
+        Drawable icon = getDrawableForDensity(iconRes, density);
+        // Get the default density icon
+        if (icon == null) {
+            icon = mResolveInfo.loadIcon(mPm);
+        }
+        return icon;
+    }
+
+    /**
+     * Returns the drawable for this activity, without any badging for the profile.
+     * @param resource id of the drawable.
+     * @param density The preferred density of the icon, zero for default density. Use
+     * density DPI values from {@link DisplayMetrics}.
+     * @see DisplayMetrics
+     * @return The drawable associated with the resource id.
+     */
+    private Drawable getDrawableForDensity(int iconRes, int density) {
+        // Get the preferred density icon from the app's resources
+        if (density != 0 && iconRes != 0) {
+            try {
+                final Resources resources
+                        = mPm.getResourcesForApplication(mActivityInfo.applicationInfo);
+                return resources.getDrawableForDensity(iconRes, density);
+            } catch (NameNotFoundException | Resources.NotFoundException exc) {
+            }
+        }
+        return null;
+    }
+
+    /**
      * Returns the application flags from the ApplicationInfo of the activity.
      *
      * @return Application flags
@@ -167,7 +198,7 @@
      * @return A badged icon for the activity.
      */
     public Drawable getBadgedIcon(int density) {
-        Drawable originalIcon = getIcon(density);
+        Drawable originalIcon = getOriginalIcon(density);
 
         if (originalIcon instanceof BitmapDrawable) {
             return mPm.getUserBadgedIcon(originalIcon, mUser);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 51fa075..6401fe6 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1888,6 +1888,57 @@
     public static final String EXTRA_FAILURE_EXISTING_PERMISSION
             = "android.content.pm.extra.FAILURE_EXISTING_PERMISSION";
 
+   /**
+    * Permission flag: The permission is set in its current state
+    * by the user and apps can still request it at runtime.
+    *
+    * @hide
+    */
+    @SystemApi
+    public static final int FLAG_PERMISSION_USER_SET = 1 << 0;
+
+    /**
+     * Permission flag: The permission is set in its current state
+     * by the user and it is fixed, i.e. apps can no longer request
+     * this permission.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_PERMISSION_USER_FIXED =  1 << 1;
+
+    /**
+     * Permission flag: The permission is set in its current state
+     * by device policy and neither apps nor the user can change
+     * its state.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_PERMISSION_POLICY_FIXED =  1 << 2;
+
+    /**
+     * Permission flag: The permission is set in a granted state but
+     * access to resources it guards is restricted by other means to
+     * enable revoking a permission on legacy apps that do not support
+     * runtime permissions. If this permission is upgraded to runtime
+     * because the app was updated to support runtime permissions, the
+     * the permission will be revoked in the upgrade process.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE =  1 << 3;
+
+
+    /**
+     * Mask for all permission flags.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int MASK_PERMISSION_FLAGS = 0xF;
+
     /**
      * Retrieve overall information about an application package that is
      * installed on the system.
@@ -2374,6 +2425,20 @@
      */
     public abstract void removePermission(String name);
 
+
+    /**
+     * Permission flags set when granting or revoking a permission.
+     *
+     * @hide
+     */
+    @SystemApi
+    @IntDef({FLAG_PERMISSION_USER_SET,
+            FLAG_PERMISSION_USER_FIXED,
+            FLAG_PERMISSION_POLICY_FIXED,
+            FLAG_PERMISSION_REVOKE_ON_UPGRADE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PermissionFlags {}
+
     /**
      * Grant a runtime permission to an application which the application does not
      * already have. The permission must have been requested by the application.
@@ -2389,19 +2454,20 @@
      * @param permissionName The permission name to grant.
      * @param user The user for which to grant the permission.
      *
-     * @see #revokePermission(String, String, android.os.UserHandle)
+     * @see #revokeRuntimePermission(String, String, android.os.UserHandle)
+     * @see android.content.pm.PackageManager.PermissionFlags
      *
      * @hide
      */
     @SystemApi
-    public abstract void grantPermission(@NonNull String packageName,
+    public abstract void grantRuntimePermission(@NonNull String packageName,
             @NonNull String permissionName, @NonNull UserHandle user);
 
     /**
      * Revoke a runtime permission that was previously granted by {@link
-     * #grantPermission(String, String, android.os.UserHandle)}. The permission
-     * must have been requested by and granted to the application. If the
-     * application is not allowed to hold the permission, a {@link
+     * #grantRuntimePermission(String, String, android.os.UserHandle)}. The
+     * permission must have been requested by and granted to the application.
+     * If the application is not allowed to hold the permission, a {@link
      * java.lang.SecurityException} is thrown.
      * <p>
      * <strong>Note: </strong>Using this API requires holding
@@ -2413,15 +2479,47 @@
      * @param permissionName The permission name to revoke.
      * @param user The user for which to revoke the permission.
      *
-     * @see #grantPermission(String, String, android.os.UserHandle)
+     * @see #grantRuntimePermission(String, String, android.os.UserHandle)
+     * @see android.content.pm.PackageManager.PermissionFlags
      *
      * @hide
      */
     @SystemApi
-    public abstract void revokePermission(@NonNull String packageName,
+    public abstract void revokeRuntimePermission(@NonNull String packageName,
             @NonNull String permissionName, @NonNull UserHandle user);
 
     /**
+     * Gets the state flags associated with a permission.
+     *
+     * @param permissionName The permission for which to get the flags.
+     * @param packageName The package name for which to get the flags.
+     * @param user The user for which to get permission flags.
+     * @return The permission flags.
+     *
+     * @hide
+     */
+    @SystemApi
+    public abstract @PermissionFlags int getPermissionFlags(String permissionName,
+            String packageName, @NonNull UserHandle user);
+
+    /**
+     * Updates the flags associated with a permission by replacing the flags in
+     * the specified mask with the provided flag values.
+     *
+     * @param permissionName The permission for which to update the flags.
+     * @param packageName The package name for which to update the flags.
+     * @param flagMask The flags which to replace.
+     * @param flagValues The flags with which to replace.
+     * @param user The user for which to update the permission flags.
+     *
+     * @hide
+     */
+    @SystemApi
+    public abstract void updatePermissionFlags(String permissionName,
+            String packageName, @PermissionFlags int flagMask, int flagValues,
+            @NonNull UserHandle user);
+
+    /**
      * Returns an {@link android.content.Intent} suitable for passing to
      * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)}
      * which prompts the user to grant permissions to this application.
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index 05f5e90..649fdb4 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -221,16 +221,16 @@
         }
         return ci.loadIcon(pm);
     }
-    
+
     /**
      * Return the icon resource identifier to use for this match.  If the
      * match defines an icon, that is used; else if the activity defines
      * an icon, that is used; else, the application icon is used.
-     * 
+     * This function does not check noResourceId flag.
+     *
      * @return The icon associated with this match.
      */
-    public final int getIconResource() {
-        if (noResourceId) return 0;
+    final int getIconResourceInternal() {
         if (icon != 0) return icon;
         final ComponentInfo ci = getComponentInfo();
         if (ci != null) {
@@ -239,6 +239,18 @@
         return 0;
     }
 
+    /**
+     * Return the icon resource identifier to use for this match.  If the
+     * match defines an icon, that is used; else if the activity defines
+     * an icon, that is used; else, the application icon is used.
+     *
+     * @return The icon associated with this match.
+     */
+    public final int getIconResource() {
+        if (noResourceId) return 0;
+        return getIconResourceInternal();
+    }
+
     public void dump(Printer pw, String prefix) {
         if (filter != null) {
             pw.println(prefix + "Filter:");
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index d8c3361..26878c0 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -496,6 +496,8 @@
      * Tests if a given integer represents a valid network type.
      * @param networkType the type to be tested
      * @return a boolean.  {@code true} if the type is valid, else {@code false}
+     * @deprecated All APIs accepting a network type are deprecated. There should be no need to
+     *             validate a network type.
      */
     public static boolean isNetworkTypeValid(int networkType) {
         return networkType >= 0 && networkType <= MAX_NETWORK_TYPE;
diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl
index 3cb29ff..602bfea 100644
--- a/core/java/android/os/IDeviceIdleController.aidl
+++ b/core/java/android/os/IDeviceIdleController.aidl
@@ -23,4 +23,5 @@
     String[] getSystemPowerWhitelist();
     String[] getFullPowerWhitelist();
     int[] getAppIdWhitelist();
+    boolean isPowerSaveWhitelistApp(String name);
 }
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 00ab262..9a0d0d0 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -134,4 +134,6 @@
     }
 
     public abstract void setDeviceIdleMode(boolean enabled);
+
+    public abstract void setDeviceIdleWhitelist(int[] appids);
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index dc70d7b..d3a5561 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5517,6 +5517,12 @@
         public static final String APP_IDLE_DURATION = "app_idle_duration";
 
         /**
+         * Controls whether double tap to wake is enabled.
+         * @hide
+         */
+        public static final String DOUBLE_TAP_TO_WAKE = "double_tap_to_wake";
+
+        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
@@ -5571,7 +5577,8 @@
             MOUNT_UMS_PROMPT,
             MOUNT_UMS_NOTIFY_ENABLED,
             UI_NIGHT_MODE,
-            SLEEP_TIMEOUT
+            SLEEP_TIMEOUT,
+            DOUBLE_TAP_TO_WAKE,
         };
 
         /**
diff --git a/core/java/android/service/chooser/ChooserTarget.java b/core/java/android/service/chooser/ChooserTarget.java
index f0ca276..4c94ee7 100644
--- a/core/java/android/service/chooser/ChooserTarget.java
+++ b/core/java/android/service/chooser/ChooserTarget.java
@@ -58,12 +58,6 @@
     private IntentSender mIntentSender;
 
     /**
-     * A raw intent provided in lieu of an IntentSender. Will be filled in and sent
-     * by {@link #sendIntent(Context, Intent)}.
-     */
-    private Intent mIntent;
-
-    /**
      * The score given to this item. It can be normalized.
      */
     private float mScore;
@@ -146,43 +140,6 @@
         mIntentSender = intentSender;
     }
 
-    /**
-     * Construct a deep link target for presentation by a chooser UI.
-     *
-     * <p>A target is composed of a title and an icon for presentation to the user.
-     * The UI presenting this target may truncate the title if it is too long to be presented
-     * in the available space, as well as crop, resize or overlay the supplied icon.</p>
-     *
-     * <p>The creator of a target may supply a ranking score. This score is assumed to be relative
-     * to the other targets supplied by the same
-     * {@link ChooserTargetService#onGetChooserTargets(ComponentName, IntentFilter) query}.
-     * Scores should be in the range from 0.0f (unlikely match) to 1.0f (very relevant match).
-     * Scores for a set of targets do not need to sum to 1.</p>
-     *
-     * <p>Before being sent, the Intent supplied will be
-     * {@link Intent#fillIn(Intent, int) filled in} by the Intent originally supplied
-     * to the chooser.</p>
-     *
-     * <p>Take care not to place custom {@link android.os.Parcelable} types into
-     * the Intent as extras, as the system will not be able to unparcel it to merge
-     * additional extras.</p>
-     *
-     * @param title title of this target that will be shown to a user
-     * @param icon icon to represent this target
-     * @param score ranking score for this target between 0.0f and 1.0f, inclusive
-     * @param intent Intent to fill in and send if the user chooses this target
-     */
-    public ChooserTarget(CharSequence title, Bitmap icon, float score, Intent intent) {
-        mTitle = title;
-        mIcon = icon;
-        if (score > 1.f || score < 0.f) {
-            throw new IllegalArgumentException("Score " + score + " out of range; "
-                    + "must be between 0.0f and 1.0f");
-        }
-        mScore = score;
-        mIntent = intent;
-    }
-
     ChooserTarget(Parcel in) {
         mTitle = in.readCharSequence();
         if (in.readInt() != 0) {
@@ -192,9 +149,6 @@
         }
         mScore = in.readFloat();
         mIntentSender = IntentSender.readIntentSenderOrNullFromParcel(in);
-        if (in.readInt() != 0) {
-            mIntent = Intent.CREATOR.createFromParcel(in);
-        }
     }
 
     /**
@@ -241,18 +195,6 @@
     }
 
     /**
-     * Returns the Intent supplied by the ChooserTarget's creator.
-     * This may be null if the creator specified an IntentSender or PendingIntent instead.
-     *
-     * <p>To fill in and send the intent, see {@link #sendIntent(Context, Intent)}.</p>
-     *
-     * @return the Intent supplied by the ChooserTarget's creator
-     */
-    public Intent getIntent() {
-        return mIntent;
-    }
-
-    /**
      * Fill in the IntentSender supplied by the ChooserTarget's creator and send it.
      *
      * @param context the sending Context; generally the Activity presenting the chooser UI
@@ -272,91 +214,8 @@
                 Log.e(TAG, "sendIntent " + this + " failed", e);
                 return false;
             }
-        } else if (mIntent != null) {
-            try {
-                final Intent toSend = new Intent(mIntent);
-                toSend.fillIn(fillInIntent, 0);
-                context.startActivity(toSend);
-                return true;
-            } catch (Exception e) {
-                Log.e(TAG, "sendIntent " + this + " failed", e);
-                return false;
-            }
         } else {
-            Log.e(TAG, "sendIntent " + this + " failed - no IntentSender or Intent to send");
-            return false;
-        }
-    }
-
-    /**
-     * Same as {@link #sendIntent(Context, Intent)}, but offers a userId field to use
-     * for launching the {@link #getIntent() intent} using
-     * {@link Activity#startActivityAsCaller(Intent, Bundle, int)} if the
-     * {@link #getIntentSender() IntentSender} is not present. If the IntentSender is present,
-     * it will be invoked as usual with its own calling identity.
-     *
-     * @hide internal use only.
-     */
-    public boolean sendIntentAsCaller(Activity context, Intent fillInIntent, int userId) {
-        if (fillInIntent != null) {
-            fillInIntent.migrateExtraStreamToClipData();
-            fillInIntent.prepareToLeaveProcess();
-        }
-        if (mIntentSender != null) {
-            try {
-                mIntentSender.sendIntent(context, 0, fillInIntent, null, null);
-                return true;
-            } catch (IntentSender.SendIntentException e) {
-                Log.e(TAG, "sendIntent " + this + " failed", e);
-                return false;
-            }
-        } else if (mIntent != null) {
-            try {
-                final Intent toSend = new Intent(mIntent);
-                toSend.fillIn(fillInIntent, 0);
-                context.startActivityAsCaller(toSend, null, userId);
-                return true;
-            } catch (Exception e) {
-                Log.e(TAG, "sendIntent " + this + " failed", e);
-                return false;
-            }
-        } else {
-            Log.e(TAG, "sendIntent " + this + " failed - no IntentSender or Intent to send");
-            return false;
-        }
-    }
-
-    /**
-     * The UserHandle is only used if we're launching a raw intent. The IntentSender will be
-     * launched with its associated identity.
-     *
-     * @hide Internal use only
-     */
-    public boolean sendIntentAsUser(Activity context, Intent fillInIntent, UserHandle user) {
-        if (fillInIntent != null) {
-            fillInIntent.migrateExtraStreamToClipData();
-            fillInIntent.prepareToLeaveProcess();
-        }
-        if (mIntentSender != null) {
-            try {
-                mIntentSender.sendIntent(context, 0, fillInIntent, null, null);
-                return true;
-            } catch (IntentSender.SendIntentException e) {
-                Log.e(TAG, "sendIntent " + this + " failed", e);
-                return false;
-            }
-        } else if (mIntent != null) {
-            try {
-                final Intent toSend = new Intent(mIntent);
-                toSend.fillIn(fillInIntent, 0);
-                context.startActivityAsUser(toSend, user);
-                return true;
-            } catch (Exception e) {
-                Log.e(TAG, "sendIntent " + this + " failed", e);
-                return false;
-            }
-        } else {
-            Log.e(TAG, "sendIntent " + this + " failed - no IntentSender or Intent to send");
+            Log.e(TAG, "sendIntent " + this + " failed - no IntentSender to send");
             return false;
         }
     }
@@ -364,7 +223,7 @@
     @Override
     public String toString() {
         return "ChooserTarget{"
-                + (mIntentSender != null ? mIntentSender.getCreatorPackage() : mIntent)
+                + (mIntentSender != null ? mIntentSender.getCreatorPackage() : null)
                 + ", "
                 + "'" + mTitle
                 + "', " + mScore + "}";
@@ -386,10 +245,6 @@
         }
         dest.writeFloat(mScore);
         IntentSender.writeIntentSenderOrNullToParcel(mIntentSender, dest);
-        dest.writeInt(mIntent != null ? 1 : 0);
-        if (mIntent != null) {
-            mIntent.writeToParcel(dest, 0);
-        }
     }
 
     public static final Creator<ChooserTarget> CREATOR
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 1ac3f45..f6ce353 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1817,9 +1817,7 @@
     public static final boolean isWakeKey(int keyCode) {
         switch (keyCode) {
             case KeyEvent.KEYCODE_BACK:
-            case KeyEvent.KEYCODE_POWER:
             case KeyEvent.KEYCODE_MENU:
-            case KeyEvent.KEYCODE_SLEEP:
             case KeyEvent.KEYCODE_WAKEUP:
             case KeyEvent.KEYCODE_PAIRING:
                 return true;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 51c4760..b476e9b 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -586,6 +586,8 @@
         mGroupFlags |= FLAG_CLIP_CHILDREN;
         mGroupFlags |= FLAG_CLIP_TO_PADDING;
         mGroupFlags |= FLAG_ANIMATION_DONE;
+        mGroupFlags |= FLAG_ANIMATION_CACHE;
+        mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
 
         if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB) {
             mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index 6feb94b..bb4a948 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -244,7 +244,7 @@
                 @Override
                 public void onClick(DialogInterface dialog, int which) {
                     PackageManager pm = getContext().getPackageManager();
-                    pm.revokePermission(mPackageName, mPerm.name,
+                    pm.revokeRuntimePermission(mPackageName, mPerm.name,
                             new UserHandle(mContext.getUserId()));
                     PermissionItemView.this.setVisibility(View.GONE);
                 }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4fd85b6..2e36cee 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3902,6 +3902,9 @@
         @Override
         public void updatePosition(float x, float y) {
             positionAtCursorOffset(mTextView.getOffsetForPosition(x, y), false);
+            if (mSelectionActionMode != null) {
+                mSelectionActionMode.invalidate();
+            }
         }
 
         @Override
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 62ca1f0..83fa967 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -24,9 +24,11 @@
 import android.content.IntentSender.SendIntentException;
 import android.content.ServiceConnection;
 import android.content.pm.ActivityInfo;
+import android.content.pm.LabeledIntent;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
+import android.database.DataSetObserver;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -37,6 +39,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.service.chooser.ChooserTarget;
 import android.service.chooser.ChooserTargetService;
 import android.service.chooser.IChooserTargetResult;
@@ -44,8 +47,16 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.AbsListView;
+import android.widget.BaseAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import com.android.internal.R;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -63,7 +74,7 @@
     private IntentSender mRefinementIntentSender;
     private RefinementResultReceiver mRefinementResultReceiver;
 
-    private ChooserTarget[] mCallerChooserTargets;
+    private ChooserListAdapter mChooserListAdapter;
 
     private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>();
 
@@ -84,8 +95,7 @@
                                 + " Have you considered returning results faster?");
                         break;
                     }
-                    final ChooserListAdapter cla = (ChooserListAdapter) getAdapter();
-                    cla.addServiceResults(sri.originalTarget, sri.resultTargets);
+                    mChooserListAdapter.addServiceResults(sri.originalTarget, sri.resultTargets);
                     unbindService(sri.connection);
                     mServiceConnections.remove(sri.connection);
                     break;
@@ -166,20 +176,6 @@
             }
         }
 
-        pa = intent.getParcelableArrayExtra(Intent.EXTRA_CHOOSER_TARGETS);
-        if (pa != null) {
-            final ChooserTarget[] targets = new ChooserTarget[pa.length];
-            for (int i = 0; i < pa.length; i++) {
-                if (!(pa[i] instanceof ChooserTarget)) {
-                    Log.w(TAG, "Chooser target #" + i + " is not a ChooserTarget: " + pa[i]);
-                    finish();
-                    super.onCreate(null);
-                    return;
-                }
-                targets[i] = (ChooserTarget) pa[i];
-            }
-            mCallerChooserTargets = targets;
-        }
         mChosenComponentSender = intent.getParcelableExtra(
                 Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER);
         mRefinementIntentSender = intent.getParcelableExtra(
@@ -233,8 +229,19 @@
     }
 
     @Override
+    void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter,
+            boolean alwaysUseOption) {
+        final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null;
+        mChooserListAdapter = (ChooserListAdapter) adapter;
+        adapterView.setAdapter(new ChooserRowAdapter(mChooserListAdapter));
+        if (listView != null) {
+            listView.setItemsCanFocus(true);
+        }
+    }
+
+    @Override
     int getLayoutResource() {
-        return com.android.internal.R.layout.chooser_grid;
+        return R.layout.chooser_grid;
     }
 
     @Override
@@ -413,10 +420,11 @@
     }
 
     @Override
-    ResolveListAdapter createAdapter(Context context, Intent[] initialIntents,
-            List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) {
-        final ChooserListAdapter adapter = new ChooserListAdapter(context, initialIntents, rList,
-                launchedFromUid, filterLastUsed, mCallerChooserTargets);
+    ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents,
+            Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
+            boolean filterLastUsed) {
+        final ChooserListAdapter adapter = new ChooserListAdapter(context, payloadIntents,
+                initialIntents, rList, launchedFromUid, filterLastUsed);
         if (DEBUG) Log.d(TAG, "Adapter created; querying services");
         queryTargetServices(adapter);
         return adapter;
@@ -426,17 +434,23 @@
         private final DisplayResolveInfo mSourceInfo;
         private final ResolveInfo mBackupResolveInfo;
         private final ChooserTarget mChooserTarget;
+        private Drawable mBadgeIcon = null;
         private final Drawable mDisplayIcon;
         private final Intent mFillInIntent;
         private final int mFillInFlags;
 
-        public ChooserTargetInfo(ChooserTarget target) {
-            this(null, target);
-        }
-
         public ChooserTargetInfo(DisplayResolveInfo sourceInfo, ChooserTarget chooserTarget) {
             mSourceInfo = sourceInfo;
             mChooserTarget = chooserTarget;
+            if (sourceInfo != null) {
+                final ResolveInfo ri = sourceInfo.getResolveInfo();
+                if (ri != null) {
+                    final ActivityInfo ai = ri.activityInfo;
+                    if (ai != null && ai.applicationInfo != null) {
+                        mBadgeIcon = getPackageManager().getApplicationIcon(ai.applicationInfo);
+                    }
+                }
+            }
             mDisplayIcon = new BitmapDrawable(getResources(), chooserTarget.getIcon());
 
             if (sourceInfo != null) {
@@ -453,6 +467,7 @@
             mSourceInfo = other.mSourceInfo;
             mBackupResolveInfo = other.mBackupResolveInfo;
             mChooserTarget = other.mChooserTarget;
+            mBadgeIcon = other.mBadgeIcon;
             mDisplayIcon = other.mDisplayIcon;
             mFillInIntent = fillInIntent;
             mFillInFlags = flags;
@@ -460,10 +475,7 @@
 
         @Override
         public Intent getResolvedIntent() {
-            final Intent targetIntent = mChooserTarget.getIntent();
-            if (targetIntent != null) {
-                return targetIntent;
-            } else if (mSourceInfo != null) {
+            if (mSourceInfo != null) {
                 return mSourceInfo.getResolvedIntent();
             }
             return getTargetIntent();
@@ -507,7 +519,8 @@
             if (intent == null) {
                 return false;
             }
-            return mChooserTarget.sendIntentAsCaller(activity, intent, userId);
+            // ChooserTargets will launch with their IntentSender's identity
+            return mChooserTarget.sendIntent(activity, intent);
         }
 
         @Override
@@ -516,7 +529,8 @@
             if (intent == null) {
                 return false;
             }
-            return mChooserTarget.sendIntentAsUser(activity, intent, user);
+            // ChooserTargets will launch with their IntentSender's identity
+            return mChooserTarget.sendIntent(activity, intent);
         }
 
         @Override
@@ -540,6 +554,11 @@
         }
 
         @Override
+        public Drawable getBadgeIcon() {
+            return mBadgeIcon;
+        }
+
+        @Override
         public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) {
             return new ChooserTargetInfo(this, fillInIntent, flags);
         }
@@ -556,16 +575,49 @@
     }
 
     public class ChooserListAdapter extends ResolveListAdapter {
+        public static final int TARGET_BAD = -1;
+        public static final int TARGET_CALLER = 0;
+        public static final int TARGET_SERVICE = 1;
+        public static final int TARGET_STANDARD = 2;
+
         private final List<ChooserTargetInfo> mServiceTargets = new ArrayList<>();
-        private final List<ChooserTargetInfo> mCallerTargets = new ArrayList<>();
+        private final List<TargetInfo> mCallerTargets = new ArrayList<>();
 
-        public ChooserListAdapter(Context context, Intent[] initialIntents, List<ResolveInfo> rList,
-                int launchedFromUid, boolean filterLastUsed, ChooserTarget[] callerChooserTargets) {
-            super(context, initialIntents, rList, launchedFromUid, filterLastUsed);
+        public ChooserListAdapter(Context context, List<Intent> payloadIntents,
+                Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
+                boolean filterLastUsed) {
+            // Don't send the initial intents through the shared ResolverActivity path,
+            // we want to separate them into a different section.
+            super(context, payloadIntents, null, rList, launchedFromUid, filterLastUsed);
 
-            if (callerChooserTargets != null) {
-                for (ChooserTarget target : callerChooserTargets) {
-                    mCallerTargets.add(new ChooserTargetInfo(target));
+            if (initialIntents != null) {
+                final PackageManager pm = getPackageManager();
+                for (int i = 0; i < initialIntents.length; i++) {
+                    final Intent ii = initialIntents[i];
+                    if (ii == null) {
+                        continue;
+                    }
+                    final ActivityInfo ai = ii.resolveActivityInfo(pm, 0);
+                    if (ai == null) {
+                        Log.w(TAG, "No activity found for " + ii);
+                        continue;
+                    }
+                    ResolveInfo ri = new ResolveInfo();
+                    ri.activityInfo = ai;
+                    UserManager userManager =
+                            (UserManager) getSystemService(Context.USER_SERVICE);
+                    if (userManager.isManagedProfile()) {
+                        ri.noResourceId = true;
+                    }
+                    if (ii instanceof LabeledIntent) {
+                        LabeledIntent li = (LabeledIntent)ii;
+                        ri.resolvePackageName = li.getSourcePackage();
+                        ri.labelRes = li.getLabelResource();
+                        ri.nonLocalizedLabel = li.getNonLocalizedLabel();
+                        ri.icon = li.getIconResource();
+                    }
+                    mCallerTargets.add(new DisplayResolveInfo(ii, ri,
+                            ri.loadLabel(pm), null, ii));
                 }
             }
         }
@@ -578,7 +630,7 @@
         }
 
         @Override
-        public View createView(ViewGroup parent) {
+        public View onCreateView(ViewGroup parent) {
             return mInflater.inflate(
                     com.android.internal.R.layout.resolve_grid_item, parent, false);
         }
@@ -600,6 +652,41 @@
             return super.getCount() + mServiceTargets.size() + mCallerTargets.size();
         }
 
+        public int getCallerTargetsCount() {
+            return mCallerTargets.size();
+        }
+
+        public int getServiceTargetsCount() {
+            return mServiceTargets.size();
+        }
+
+        public int getStandardTargetCount() {
+            return super.getCount();
+        }
+
+        public int getPositionTargetType(int position) {
+            int offset = 0;
+
+            final int callerTargetCount = mCallerTargets.size();
+            if (position < callerTargetCount) {
+                return TARGET_CALLER;
+            }
+            offset += callerTargetCount;
+
+            final int serviceTargetCount = mServiceTargets.size();
+            if (position - offset < serviceTargetCount) {
+                return TARGET_SERVICE;
+            }
+            offset += serviceTargetCount;
+
+            final int standardTargetCount = super.getCount();
+            if (position - offset < standardTargetCount) {
+                return TARGET_STANDARD;
+            }
+
+            return TARGET_BAD;
+        }
+
         @Override
         public TargetInfo getItem(int position) {
             int offset = 0;
@@ -643,6 +730,133 @@
         }
     }
 
+    class ChooserRowAdapter extends BaseAdapter {
+        private ChooserListAdapter mChooserListAdapter;
+        private final LayoutInflater mLayoutInflater;
+        private final int mColumnCount = 4;
+
+        public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) {
+            mChooserListAdapter = wrappedAdapter;
+            mLayoutInflater = LayoutInflater.from(ChooserActivity.this);
+
+            wrappedAdapter.registerDataSetObserver(new DataSetObserver() {
+                @Override
+                public void onChanged() {
+                    super.onChanged();
+                    notifyDataSetChanged();
+                }
+
+                @Override
+                public void onInvalidated() {
+                    super.onInvalidated();
+                    notifyDataSetInvalidated();
+                }
+            });
+        }
+
+        @Override
+        public int getCount() {
+            return (int) (
+                    Math.ceil((float) mChooserListAdapter.getCallerTargetsCount() / mColumnCount)
+                    + Math.ceil((float) mChooserListAdapter.getServiceTargetsCount() / mColumnCount)
+                    + Math.ceil((float) mChooserListAdapter.getStandardTargetCount() / mColumnCount)
+            );
+        }
+
+        @Override
+        public Object getItem(int position) {
+            // We have nothing useful to return here.
+            return position;
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            final View[] holder;
+            if (convertView == null) {
+                holder = createViewHolder(parent);
+            } else {
+                holder = (View[]) convertView.getTag();
+            }
+            bindViewHolder(position, holder);
+
+            // We keep the actual list item view as the last item in the holder array
+            return holder[mColumnCount];
+        }
+
+        View[] createViewHolder(ViewGroup parent) {
+            final View[] holder = new View[mColumnCount + 1];
+
+            final ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row,
+                    parent, false);
+            for (int i = 0; i < mColumnCount; i++) {
+                holder[i] = mChooserListAdapter.createView(row);
+                row.addView(holder[i]);
+            }
+            row.setTag(holder);
+            holder[mColumnCount] = row;
+            return holder;
+        }
+
+        void bindViewHolder(int rowPosition, View[] holder) {
+            final int start = getFirstRowPosition(rowPosition);
+            final int startType = mChooserListAdapter.getPositionTargetType(start);
+
+            int end = start + mColumnCount - 1;
+            while (mChooserListAdapter.getPositionTargetType(end) != startType && end >= start) {
+                end--;
+            }
+
+            final ViewGroup row = (ViewGroup) holder[mColumnCount];
+
+            if (startType == ChooserListAdapter.TARGET_SERVICE) {
+                row.setBackgroundColor(getColor(R.color.chooser_service_row_background_color));
+            } else {
+                row.setBackground(null);
+            }
+
+            for (int i = 0; i < mColumnCount; i++) {
+                final View v = holder[i];
+                if (start + i <= end) {
+                    v.setVisibility(View.VISIBLE);
+                    final int itemIndex = start + i;
+                    mChooserListAdapter.bindView(itemIndex, v);
+                    v.setOnClickListener(new OnClickListener() {
+                        @Override
+                        public void onClick(View v) {
+                            startSelected(itemIndex, false, true);
+                        }
+                    });
+                } else {
+                    v.setVisibility(View.GONE);
+                }
+            }
+        }
+
+        int getFirstRowPosition(int row) {
+            final int callerCount = mChooserListAdapter.getCallerTargetsCount();
+            final int callerRows = (int) Math.ceil((float) callerCount / mColumnCount);
+
+            if (row < callerRows) {
+                return row * mColumnCount;
+            }
+
+            final int serviceCount = mChooserListAdapter.getServiceTargetsCount();
+            final int serviceRows = (int) Math.ceil((float) serviceCount / mColumnCount);
+
+            if (row < callerRows + serviceRows) {
+                return callerCount + (row - callerRows) * mColumnCount;
+            }
+
+            return callerCount + serviceCount
+                    + (row - callerRows - serviceRows) * mColumnCount;
+        }
+    }
+
     class ChooserTargetServiceConnection implements ServiceConnection {
         private final DisplayResolveInfo mOriginalTarget;
 
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 20486643..4696757 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -25,7 +25,6 @@
 import android.text.TextUtils;
 import android.util.Slog;
 import android.widget.AbsListView;
-import android.widget.GridView;
 import com.android.internal.R;
 import com.android.internal.content.PackageMonitor;
 
@@ -83,7 +82,7 @@
  * which there is more than one matching activity, allowing the user to decide
  * which to go to.  It is not normally used directly by application developers.
  */
-public class ResolverActivity extends Activity implements AdapterView.OnItemClickListener {
+public class ResolverActivity extends Activity {
     private static final String TAG = "ResolverActivity";
     private static final boolean DEBUG = false;
 
@@ -93,8 +92,6 @@
     private boolean mSafeForwardingMode;
     private boolean mAlwaysUseOption;
     private AbsListView mAdapterView;
-    private ListView mListView;
-    private GridView mGridView;
     private Button mAlwaysButton;
     private Button mOnceButton;
     private View mProfileView;
@@ -217,6 +214,13 @@
         } catch (RemoteException e) {
             mLaunchedFromUid = -1;
         }
+
+        if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
+            // Gulp!
+            finish();
+            return;
+        }
+
         mPm = getPackageManager();
         mUsm = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
 
@@ -229,67 +233,11 @@
         final ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
         mIconDpi = am.getLauncherLargeIconDensity();
 
+        // Add our initial intent as the first item, regardless of what else has already been added.
         mIntents.add(0, new Intent(intent));
-        mAdapter = createAdapter(this, initialIntents, rList, mLaunchedFromUid, alwaysUseOption);
 
-        final int layoutId;
-        final boolean useHeader;
-        if (mAdapter.hasFilteredItem()) {
-            layoutId = R.layout.resolver_list_with_default;
-            alwaysUseOption = false;
-            useHeader = true;
-        } else {
-            useHeader = false;
-            layoutId = getLayoutResource();
-        }
-        mAlwaysUseOption = alwaysUseOption;
+        configureContentView(mIntents, initialIntents, rList, alwaysUseOption);
 
-        if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
-            // Gulp!
-            finish();
-            return;
-        }
-
-        int count = mAdapter.mDisplayList.size();
-        if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) {
-            setContentView(layoutId);
-            mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
-            mAdapterView.setAdapter(mAdapter);
-            mAdapterView.setOnItemClickListener(this);
-            mAdapterView.setOnItemLongClickListener(new ItemLongClickListener());
-
-            // Initialize the different types of collection views we may have. Depending
-            // on which ones are initialized later we'll configure different properties.
-            if (mAdapterView instanceof ListView) {
-                mListView = (ListView) mAdapterView;
-            }
-            if (mAdapterView instanceof GridView) {
-                mGridView = (GridView) mAdapterView;
-            }
-
-            if (alwaysUseOption) {
-                mAdapterView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
-            }
-
-            if (useHeader && mListView != null) {
-                mListView.addHeaderView(LayoutInflater.from(this).inflate(
-                        R.layout.resolver_different_item_header, mListView, false));
-            }
-        } else if (count == 1) {
-            safelyStartActivity(mAdapter.targetInfoForPosition(0, false));
-            mPackageMonitor.unregister();
-            mRegistered = false;
-            finish();
-            return;
-        } else {
-            setContentView(R.layout.resolver_list);
-
-            final TextView empty = (TextView) findViewById(R.id.empty);
-            empty.setVisibility(View.VISIBLE);
-
-            mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
-            mAdapterView.setVisibility(View.GONE);
-        }
         // Prevent the Resolver window from becoming the top fullscreen window and thus from taking
         // control of the system bars.
         getWindow().clearFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR);
@@ -548,29 +496,6 @@
         }
     }
 
-    @Override
-    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-        if (mListView != null) {
-            position -= mListView.getHeaderViewsCount();
-        }
-        if (position < 0) {
-            // Header views don't count.
-            return;
-        }
-        final int checkedPos = mAdapterView.getCheckedItemPosition();
-        final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
-        if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {
-            setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
-            mOnceButton.setEnabled(hasValidSelection);
-            if (hasValidSelection) {
-                mAdapterView.smoothScrollToPosition(checkedPos);
-            }
-            mLastSelected = checkedPos;
-        } else {
-            startSelected(position, false, true);
-        }
-    }
-
     private boolean hasManagedProfile() {
         UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
         if (userManager == null) {
@@ -743,29 +668,37 @@
                     if (r.match > bestMatch) bestMatch = r.match;
                 }
                 if (alwaysCheck) {
-                    PackageManager pm = getPackageManager();
+                    final int userId = getUserId();
+                    final PackageManager pm = getPackageManager();
 
                     // Set the preferred Activity
                     pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent());
 
-                    // Update Domain Verification status
-                    int userId = getUserId();
-                    ComponentName cn = intent.getComponent();
-                    String packageName = cn.getPackageName();
-                    String dataScheme = (data != null) ? data.getScheme() : null;
+                    if (ri.handleAllWebDataURI) {
+                        // Set default Browser if needed
+                        final String packageName = pm.getDefaultBrowserPackageName(userId);
+                        if (TextUtils.isEmpty(packageName)) {
+                            pm.setDefaultBrowserPackageName(ri.activityInfo.packageName, userId);
+                        }
+                    } else {
+                        // Update Domain Verification status
+                        ComponentName cn = intent.getComponent();
+                        String packageName = cn.getPackageName();
+                        String dataScheme = (data != null) ? data.getScheme() : null;
 
-                    boolean isHttpOrHttps = (dataScheme != null) &&
-                            (dataScheme.equals(IntentFilter.SCHEME_HTTP) ||
-                            dataScheme.equals(IntentFilter.SCHEME_HTTPS));
+                        boolean isHttpOrHttps = (dataScheme != null) &&
+                                (dataScheme.equals(IntentFilter.SCHEME_HTTP) ||
+                                        dataScheme.equals(IntentFilter.SCHEME_HTTPS));
 
-                    boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW);
-                    boolean hasCategoryBrowsable = (categories != null) &&
-                            categories.contains(Intent.CATEGORY_BROWSABLE);
+                        boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW);
+                        boolean hasCategoryBrowsable = (categories != null) &&
+                                categories.contains(Intent.CATEGORY_BROWSABLE);
 
-                    if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) {
-                        pm.updateIntentVerificationStatus(packageName,
-                                PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
-                                userId);
+                        if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) {
+                            pm.updateIntentVerificationStatus(packageName,
+                                    PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
+                                    userId);
+                        }
                     }
                 } else {
                     try {
@@ -831,14 +764,68 @@
         startActivity(in);
     }
 
-    ResolveListAdapter createAdapter(Context context, Intent[] initialIntents,
-            List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) {
-        return new ResolveListAdapter(context, initialIntents, rList, launchedFromUid,
-                filterLastUsed);
+    ResolveListAdapter createAdapter(Context context, List<Intent> payloadIntents,
+            Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
+            boolean filterLastUsed) {
+        return new ResolveListAdapter(context, payloadIntents, initialIntents, rList,
+                launchedFromUid, filterLastUsed);
     }
 
-    ResolveListAdapter getAdapter() {
-        return mAdapter;
+    void configureContentView(List<Intent> payloadIntents, Intent[] initialIntents,
+            List<ResolveInfo> rList, boolean alwaysUseOption) {
+        mAdapter = createAdapter(this, payloadIntents, initialIntents, rList,
+                mLaunchedFromUid, alwaysUseOption);
+
+        final int layoutId;
+        if (mAdapter.hasFilteredItem()) {
+            layoutId = R.layout.resolver_list_with_default;
+            alwaysUseOption = false;
+        } else {
+            layoutId = getLayoutResource();
+        }
+        mAlwaysUseOption = alwaysUseOption;
+
+        int count = mAdapter.mDisplayList.size();
+        if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) {
+            setContentView(layoutId);
+            mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
+            onPrepareAdapterView(mAdapterView, mAdapter, alwaysUseOption);
+        } else if (count == 1) {
+            safelyStartActivity(mAdapter.targetInfoForPosition(0, false));
+            mPackageMonitor.unregister();
+            mRegistered = false;
+            finish();
+            return;
+        } else {
+            setContentView(R.layout.resolver_list);
+
+            final TextView empty = (TextView) findViewById(R.id.empty);
+            empty.setVisibility(View.VISIBLE);
+
+            mAdapterView = (AbsListView) findViewById(R.id.resolver_list);
+            mAdapterView.setVisibility(View.GONE);
+        }
+    }
+
+    void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter,
+            boolean alwaysUseOption) {
+        final boolean useHeader = adapter.hasFilteredItem();
+        final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null;
+
+        adapterView.setAdapter(mAdapter);
+
+        final ItemClickListener listener = new ItemClickListener();
+        adapterView.setOnItemClickListener(listener);
+        adapterView.setOnItemLongClickListener(listener);
+
+        if (alwaysUseOption) {
+            listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
+        }
+
+        if (useHeader && listView != null) {
+            listView.addHeaderView(LayoutInflater.from(this).inflate(
+                    R.layout.resolver_different_item_header, listView, false));
+        }
     }
 
     final class DisplayResolveInfo implements TargetInfo {
@@ -888,6 +875,10 @@
             return mDisplayIcon;
         }
 
+        public Drawable getBadgeIcon() {
+            return null;
+        }
+
         @Override
         public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) {
             return new DisplayResolveInfo(this, fillInIntent, flags);
@@ -1024,6 +1015,11 @@
         public Drawable getDisplayIcon();
 
         /**
+         * @return The (small) icon to badge the target with
+         */
+        public Drawable getBadgeIcon();
+
+        /**
          * Clone this target with the given fill-in information.
          */
         public TargetInfo cloneFilledIn(Intent fillInIntent, int flags);
@@ -1035,6 +1031,7 @@
     }
 
     class ResolveListAdapter extends BaseAdapter {
+        private final List<Intent> mIntents;
         private final Intent[] mInitialIntents;
         private final List<ResolveInfo> mBaseResolveList;
         private ResolveInfo mLastChosen;
@@ -1050,8 +1047,10 @@
         private int mLastChosenPosition = -1;
         private boolean mFilterLastUsed;
 
-        public ResolveListAdapter(Context context, Intent[] initialIntents,
-                List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) {
+        public ResolveListAdapter(Context context, List<Intent> payloadIntents,
+                Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid,
+                boolean filterLastUsed) {
+            mIntents = payloadIntents;
             mInitialIntents = initialIntents;
             mBaseResolveList = rList;
             mLaunchedFromUid = launchedFromUid;
@@ -1430,15 +1429,19 @@
             View view = convertView;
             if (view == null) {
                 view = createView(parent);
-
-                final ViewHolder holder = new ViewHolder(view);
-                view.setTag(holder);
             }
-            bindView(view, getItem(position));
+            onBindView(view, getItem(position));
             return view;
         }
 
-        public View createView(ViewGroup parent) {
+        public final View createView(ViewGroup parent) {
+            final View view = onCreateView(parent);
+            final ViewHolder holder = new ViewHolder(view);
+            view.setTag(holder);
+            return view;
+        }
+
+        public View onCreateView(ViewGroup parent) {
             return mInflater.inflate(
                     com.android.internal.R.layout.resolve_list_item, parent, false);
         }
@@ -1447,7 +1450,11 @@
             return !TextUtils.isEmpty(info.getExtendedInfo());
         }
 
-        private final void bindView(View view, TargetInfo info) {
+        public final void bindView(int position, View view) {
+            onBindView(view, getItem(position));
+        }
+
+        private void onBindView(View view, TargetInfo info) {
             final ViewHolder holder = (ViewHolder) view.getTag();
             holder.text.setText(info.getDisplayLabel());
             if (showsExtendedInfo(info)) {
@@ -1461,6 +1468,15 @@
                 new LoadAdapterIconTask((DisplayResolveInfo) info).execute();
             }
             holder.icon.setImageDrawable(info.getDisplayIcon());
+            if (holder.badge != null) {
+                final Drawable badge = info.getBadgeIcon();
+                if (badge != null) {
+                    holder.badge.setImageDrawable(badge);
+                    holder.badge.setVisibility(View.VISIBLE);
+                } else {
+                    holder.badge.setVisibility(View.GONE);
+                }
+            }
         }
     }
 
@@ -1514,20 +1530,47 @@
         public TextView text;
         public TextView text2;
         public ImageView icon;
+        public ImageView badge;
 
         public ViewHolder(View view) {
             text = (TextView) view.findViewById(com.android.internal.R.id.text1);
             text2 = (TextView) view.findViewById(com.android.internal.R.id.text2);
             icon = (ImageView) view.findViewById(R.id.icon);
+            badge = (ImageView) view.findViewById(R.id.target_badge);
         }
     }
 
-    class ItemLongClickListener implements AdapterView.OnItemLongClickListener {
+    class ItemClickListener implements AdapterView.OnItemClickListener,
+            AdapterView.OnItemLongClickListener {
+        @Override
+        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+            final ListView listView = parent instanceof ListView ? (ListView) parent : null;
+            if (listView != null) {
+                position -= listView.getHeaderViewsCount();
+            }
+            if (position < 0) {
+                // Header views don't count.
+                return;
+            }
+            final int checkedPos = mAdapterView.getCheckedItemPosition();
+            final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
+            if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {
+                setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
+                mOnceButton.setEnabled(hasValidSelection);
+                if (hasValidSelection) {
+                    mAdapterView.smoothScrollToPosition(checkedPos);
+                }
+                mLastSelected = checkedPos;
+            } else {
+                startSelected(position, false, true);
+            }
+        }
 
         @Override
         public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
-            if (mListView != null) {
-                position -= mListView.getHeaderViewsCount();
+            final ListView listView = parent instanceof ListView ? (ListView) parent : null;
+            if (listView != null) {
+                position -= listView.getHeaderViewsCount();
             }
             if (position < 0) {
                 // Header views don't count.
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 0fa82eb..dcdfb6c 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -21,7 +21,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:maxWidth="@dimen/resolver_max_width"
-        android:maxCollapsedHeight="256dp"
+        android:maxCollapsedHeight="288dp"
         android:maxCollapsedHeightSmall="56dp"
         android:id="@id/contentPanel">
 
@@ -30,24 +30,25 @@
             android:layout_height="wrap_content"
             android:layout_alwaysShow="true"
             android:elevation="8dp"
-            android:paddingStart="?attr/dialogPreferredPadding"
+            android:paddingStart="16dp"
             android:background="@color/white" >
         <ImageView android:id="@+id/title_icon"
                    android:layout_width="24dp"
                    android:layout_height="24dp"
                    android:layout_gravity="start|center_vertical"
                    android:layout_marginEnd="16dp"
+                   android:visibility="gone"
                    android:scaleType="fitCenter" />
         <TextView android:id="@+id/title"
                   android:layout_width="0dp"
                   android:layout_height="wrap_content"
                   android:layout_weight="1"
-                  android:minHeight="56dp"
                   android:textAppearance="?attr/textAppearanceMedium"
+                  android:textSize="14sp"
                   android:gravity="start|center_vertical"
                   android:paddingEnd="?attr/dialogPreferredPadding"
-                  android:paddingTop="8dp"
-                  android:paddingBottom="8dp" />
+                  android:paddingTop="12dp"
+                  android:paddingBottom="12dp" />
         <LinearLayout android:id="@+id/profile_button"
                       android:layout_width="wrap_content"
                       android:layout_height="48dp"
@@ -82,23 +83,24 @@
         </LinearLayout>
     </LinearLayout>
 
-    <GridView
+    <ListView
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:id="@+id/resolver_list"
             android:clipToPadding="false"
-            android:paddingStart="@dimen/chooser_grid_padding"
-            android:paddingEnd="@dimen/chooser_grid_padding"
             android:scrollbarStyle="outsideOverlay"
             android:background="@color/white"
             android:elevation="8dp"
-            android:numColumns="4"
+            android:listSelector="@color/transparent"
+            android:divider="@null"
+            android:scrollIndicators="top"
             android:nestedScrollingEnabled="true" />
 
     <TextView android:id="@+id/empty"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:layout_alwaysShow="true"
+              android:background="@color/white"
               android:text="@string/noApplications"
               android:padding="32dp"
               android:gravity="center"
diff --git a/core/res/res/layout/chooser_row.xml b/core/res/res/layout/chooser_row.xml
new file mode 100644
index 0000000..9baa32c
--- /dev/null
+++ b/core/res/res/layout/chooser_row.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="horizontal"
+              android:layout_width="match_parent" android:layout_height="wrap_content"
+              android:minHeight="80dp"
+              android:gravity="start|top"
+              android:paddingTop="8dp"
+              android:paddingBottom="8dp"
+              android:paddingStart="@dimen/chooser_grid_padding"
+              android:paddingEnd="@dimen/chooser_grid_padding"
+              android:weightSum="4">
+
+</LinearLayout>
+
diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml
index 664b02f..1c496f6 100644
--- a/core/res/res/layout/resolve_grid_item.xml
+++ b/core/res/res/layout/resolve_grid_item.xml
@@ -18,18 +18,31 @@
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:orientation="vertical"
-              android:layout_width="match_parent" android:layout_height="wrap_content"
+              android:layout_width="0dp"
+              android:layout_height="wrap_content"
+              android:layout_weight="1"
               android:minWidth="80dp"
               android:gravity="center"
               android:paddingTop="8dp"
               android:paddingBottom="8dp"
-              android:background="?attr/activatedBackgroundIndicator">
+              android:background="?attr/selectableItemBackgroundBorderless">
 
-    <!-- Activity icon when presenting dialog -->
-    <ImageView android:id="@+id/icon"
-               android:layout_width="48dp"
-               android:layout_height="48dp"
-               android:scaleType="fitCenter" />
+    <FrameLayout android:layout_width="wrap_content"
+                 android:layout_height="wrap_content">
+        <ImageView android:id="@+id/icon"
+                   android:layout_width="48dp"
+                   android:layout_height="48dp"
+                   android:layout_marginLeft="3dp"
+                   android:layout_marginRight="3dp"
+                   android:layout_marginBottom="3dp"
+                   android:scaleType="fitCenter" />
+        <ImageView android:id="@+id/target_badge"
+                   android:layout_width="16dp"
+                   android:layout_height="16dp"
+                   android:layout_gravity="end|bottom"
+                   android:visibility="gone"
+                   android:scaleType="fitCenter" />
+    </FrameLayout>
 
     <!-- Activity name -->
     <TextView android:id="@android:id/text1"
@@ -40,21 +53,23 @@
               android:layout_marginRight="4dp"
               android:textAppearance="?attr/textAppearanceSmall"
               android:textColor="?attr/textColorPrimary"
+              android:textSize="12sp"
               android:fontFamily="sans-serif-condensed"
-              android:gravity="center"
+              android:gravity="top|center_horizontal"
               android:minLines="2"
               android:maxLines="2"
               android:ellipsize="marquee" />
     <!-- Extended activity info to distinguish between duplicate activity names -->
     <TextView android:id="@android:id/text2"
               android:textAppearance="?android:attr/textAppearanceSmall"
+              android:textSize="12sp"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:layout_marginLeft="4dp"
               android:layout_marginRight="4dp"
               android:minLines="2"
               android:maxLines="2"
-              android:gravity="center"
+              android:gravity="top|center_horizontal"
               android:ellipsize="marquee" />
 </LinearLayout>
 
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index b9825c5..7f8c460 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -174,4 +174,6 @@
     <color name="Pink_800">#ffad1457</color>
     <color name="Red_700">#ffc53929</color>
     <color name="Red_800">#ffb93221</color>
+
+    <color name="chooser_service_row_background_color">#fff5f5f5</color>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7270b2c..690fd28 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2141,4 +2141,7 @@
 
     <!-- This config is ued to determine whether animations are allowed in low power mode. -->
     <bool name="config_allowAnimationsInLowPowerMode">false</bool>
+
+    <!-- Whether device supports double tap to wake -->
+    <bool name="config_supportDoubleTapWake">false</bool>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a3a6105..5b564e1 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -533,7 +533,7 @@
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_contacts">Contacts</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permgroupdesc_contacts">access and modify your contacts</string>
+    <string name="permgroupdesc_contacts">access your contacts</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_location">Location</string>
@@ -548,12 +548,12 @@
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_calendar">Calendar</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permgroupdesc_calendar">access and modify your calendar</string>
+    <string name="permgroupdesc_calendar">access your calendar</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_sms">SMS</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permgroupdesc_sms">access and modify SMS</string>
+    <string name="permgroupdesc_sms">view and manage SMS messages</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_dictionary">User Dictionary</string>
@@ -568,22 +568,22 @@
     <!-- Title of a category of application permissioncds, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_microphone">Microphone</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permgroupdesc_microphone">use device microphone</string>
+    <string name="permgroupdesc_microphone">record audio</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_camera">Camera</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permgroupdesc_camera">use device camera</string>
+    <string name="permgroupdesc_camera">take pictures and record video</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_phone">Phone</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permgroupdesc_phone">use device telephony</string>
+    <string name="permgroupdesc_phone">make and manage phone calls</string>
 
     <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permgrouplab_sensors">Sensors</string>
     <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permgroupdesc_sensors">access sensors and wearables</string>
+    <string name="permgroupdesc_sensors">access data from sensors and wearable devices</string>
 
     <!-- Title for the capability of an accessibility service to retrieve window content. -->
     <string name="capability_title_canRetrieveWindowContent">Retrieve window content</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 11583b3..8f949e1 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2257,4 +2257,9 @@
   <java-symbol type="id" name="day_picker_view_pager" />
   <java-symbol type="layout" name="day_picker_content_material" />
   <java-symbol type="drawable" name="scroll_indicator_material" />
+
+  <java-symbol type="layout" name="chooser_row" />
+  <java-symbol type="color" name="chooser_service_row_background_color" />
+  <java-symbol type="id" name="target_badge" />
+  <java-symbol type="bool" name="config_supportDoubleTapWake" />
 </resources>
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 7cdf72f..a352f65 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -5,29 +5,28 @@
 header.hide=1
 page.metaDescription=Download the official Android IDE and developer tools to build apps for Android phones, tablets, wearables, TVs, and more.
 
-studio.version=1.2.0.12
+studio.version=1.2.1.1
 
-studio.linux_bundle_download=android-studio-ide-141.1890965-linux.zip
-studio.linux_bundle_bytes=259139652 
-studio.linux_bundle_checksum=72149f65911ca18d8f0d360e6b7a1e2dc57e9935
+studio.linux_bundle_download=android-studio-ide-141.1903250-linux.zip
+studio.linux_bundle_bytes=258634089 
+studio.linux_bundle_checksum=61f576a24ac9aa00d498bb62942c028ef4a8905b
 
-studio.mac_bundle_download=android-studio-ide-141.1890965-mac.dmg
+studio.mac_bundle_download=android-studio-ide-141.1903250-mac.dmg
 studio.mac_bundle_bytes=260877150
-studio.mac_bundle_checksum=06fe5a2d9ab6c99bf42fb2014e519a073fc7e90d
+studio.mac_bundle_checksum=a5a6ba50e3590de0973230a238d17726a1d9395c
 
-studio.win_bundle_download=android-studio-ide-141.1890965-windows.zip
-studio.win_bundle_bytes=261548058
-studio.win_bundle_checksum=29416e54ad074881a1e668e61e3d0c2316108dbe
+studio.win_bundle_download=android-studio-ide-141.1903250-windows.zip
+studio.win_bundle_bytes=261042465
+studio.win_bundle_checksum=ce924e0e4cff4b7f24df3f7ce0c1ce2379347d72
 
 
-studio.win_bundle_exe_download=android-studio-bundle-141.1890965-windows.exe
-studio.win_bundle_exe_bytes=928285584
-studio.win_bundle_exe_checksum=47be67749409f0d710c05b9a8f22d9191c47a9d0
+studio.win_bundle_exe_download=android-studio-bundle-141.1903250-windows.exe
+studio.win_bundle_exe_bytes=930462136
+studio.win_bundle_exe_checksum=680668b6b4a51c519efda814b96c2b61541a50f2
 
-studio.win_notools_exe_download=android-studio-ide-141.1890965-windows.exe
-studio.win_notools_exe_bytes=243621072
-studio.win_notools_exe_checksum=760d45212bea42f52adb1cbeab2390156d49c74d
-
+studio.win_notools_exe_download=android-studio-ide-141.1903250-windows.exe
+studio.win_notools_exe_bytes=243747312
+studio.win_notools_exe_checksum=d89917dd044e0559c87d6a05d49780ab110269f7
 
 
 
diff --git a/docs/html/tools/debugging/ddms.jd b/docs/html/tools/debugging/ddms.jd
index e9c2877..28ad11e 100644
--- a/docs/html/tools/debugging/ddms.jd
+++ b/docs/html/tools/debugging/ddms.jd
@@ -204,6 +204,10 @@
 
     <li>Click the <strong>Start Method Profiling</strong> button.</li>
 
+    <li>In Android 4.4 and later, choose either trace-based profiling or sample-based profiling
+    with a specified sampling interval. For earlier versions of Android, only trace-based profiling
+    is available.</li>
+
     <li>Interact with your application to start the methods that you want to profile.</li>
 
     <li>Click the <strong>Stop Method Profiling</strong> button. DDMS stops profiling your
diff --git a/docs/html/tools/debugging/debugging-tracing.jd b/docs/html/tools/debugging/debugging-tracing.jd
index bd4afbc..fa5b4e1 100644
--- a/docs/html/tools/debugging/debugging-tracing.jd
+++ b/docs/html/tools/debugging/debugging-tracing.jd
@@ -127,7 +127,7 @@
   {@link android.os.Debug#startMethodTracing() startMethodTracing()} methods. In the call, you
   specify a base name for the trace files that the system generates. To stop tracing, call {@link
   android.os.Debug#stopMethodTracing() stopMethodTracing()}. These methods start and stop method
-  tracing across the entire virtual machine. For example, you could call 
+  tracing across the entire virtual machine. For example, you could call
   {@link android.os.Debug#startMethodTracing() startMethodTracing()} in
   your activity's {@link android.app.Activity#onCreate onCreate()} method, and call
   {@link android.os.Debug#stopMethodTracing() stopMethodTracing()} in that activity's
@@ -157,6 +157,13 @@
   times are only useful in relation to other profile output, so you can see if changes have made
   the code faster or slower relative to a previous profiling run.</p>
 
+  <p>In Android 4.4 and later, you can use sample-based profiling to profile with less runtime
+  performance impact. To enable sample profiling, call {@link
+  android.os.Debug#startMethodTracingSampling(java.lang.String, int, int)
+  startMethodTracingSampling()} with a specified sampling interval. The system will then gather
+  samples periodically until tracing is stopped via {@link android.os.Debug#stopMethodTracing()
+  stopMethodTracing()}.</p>
+
   <h2 id="copyingfiles">Copying Trace Files to a Host Machine</h2>
 
   <p>After your application has run and the system has created your trace files
@@ -277,7 +284,8 @@
       Traceview logging does not handle threads well, resulting in these two problems:
 
       <ol>
-        <li>If a thread exits during profiling, the thread name is not emitted;</li>
+        <li>If a thread exits during profiling, the thread name is not emitted (fixed in Android 5.1
+	and later);</li>
 
         <li>The VM reuses thread IDs. If a thread stops and another starts, they may get the same
         ID.</li>
diff --git a/docs/html/tools/revisions/studio.jd b/docs/html/tools/revisions/studio.jd
index f530a5f..7138efe 100644
--- a/docs/html/tools/revisions/studio.jd
+++ b/docs/html/tools/revisions/studio.jd
@@ -43,6 +43,21 @@
 <div class="toggle-content opened">
   <p><a href="#" onclick="return toggleContent(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+      alt=""/>Android Studio v1.2.1</a> <em>(May 2015)</em>
+  </p>
+  <div class="toggle-content-toggleme">
+    <p>Various fixes and enhancements:</p>
+    <ul>
+      <li>Fixed minor performance and feature issues. </li>
+    </ul>
+  </div>
+</div>
+
+
+
+<div class="toggle-content closed">
+  <p><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
       alt=""/>Android Studio v1.2.0</a> <em>(April 2015)</em>
   </p>
 
diff --git a/docs/html/training/testing/ui-testing/index.jd b/docs/html/training/testing/ui-testing/index.jd
index 20422f7..d660c60 100644
--- a/docs/html/training/testing/ui-testing/index.jd
+++ b/docs/html/training/testing/ui-testing/index.jd
@@ -72,5 +72,5 @@
     <dd>Learn how to test UI in a single app by using the Espresso testing framework.</dd>
   <dt><strong><a href="uiautomator-testing.html">
 Testing UI for Multiple Apps</a></strong></dt>
-    <dd>Learn how to test UI in multiple apps by using the UI Automator testing framework</dd>
+    <dd>Learn how to test UI in multiple apps by using the UI Automator testing framework.</dd>
 </dl>
\ No newline at end of file
diff --git a/docs/html/training/testing/unit-testing/index.jd b/docs/html/training/testing/unit-testing/index.jd
new file mode 100644
index 0000000..a35ba80
--- /dev/null
+++ b/docs/html/training/testing/unit-testing/index.jd
@@ -0,0 +1,63 @@
+page.title=Building Effective Unit Tests
+page.tags=testing,androidjunitrunner,junit,unit test
+
+trainingnavtop=true
+startpage=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+        <h2>
+          You should also read
+        </h2>
+        <ul>
+          <li>
+            <a href="{@docRoot}tools/testing-support-library/index.html">Testing Support Library</a>
+          </li>
+        </ul>
+</div>
+</div>
+
+<p>Unit tests are the fundamental tests in your app testing strategy. By creating and running unit
+tests against your code, you can easily verify that the logic of individual units is correct.
+Running unit tests after every build helps you to
+quickly catch and fix software regressions introduced by code changes to your app.
+</p>
+
+<p>A unit test generally exercises the functionality of the smallest possible unit of code (which
+could be a method, class, or component) in a repeatable way. You should build unit tests when you
+need to verify the logic of specific code in your app. For example, if you are unit testing a
+class, your test might check that the class is in the right state. Typically, the unit of code
+is tested in isolation; your test affects and monitors changes to that unit only. A
+<a href="http://en.wikipedia.org/wiki/Mock_object" class="external-link">mocking framework</a>
+can be used to isolate your unit from its dependencies.</p>
+
+<p class="note"><strong>Note:</strong> Unit tests are not suitable for testing
+complex UI interaction events. Instead, you should use the UI testing frameworks, as described in
+<a href="{@docRoot}training/testing/ui-testing/index.html">Automating UI Tests</a>.</p>
+
+<p>For testing Android apps, you typically create these types of automated unit tests:</p>
+
+<ul>
+<li><strong>Local tests:</strong> Unit tests that run on your local machine only. These tests are
+compiled to run locally on the Java Virtual Machine (JVM) to minimize execution time. Use this
+approach to run unit tests that have no dependencies on the Android framework or have dependencies
+that can be filled by using mock objects.</li>
+<li><strong>Instrumented tests:</strong> Unit tests that run on an Android device or emulator.
+These tests have access to instrumentation information, such as the
+{@link android.content.Context} for the app under test. Use this approach to run unit tests that
+have Android dependencies which cannot be easily filled by using mock objects.</li>
+</ul>
+
+<p>The lessons in this class teach you how to build these types of automated unit tests.</p>
+
+<h2>Lessons</h2>
+<dl>
+  <dt><strong><a href="local-unit-tests.html">
+Building Local Unit Tests</a></strong></dt>
+    <dd>Learn how to build unit tests that run on your local machine.</dd>
+  <dt><strong><a href="instrumented-unit-tests.html">
+Building Instrumented Unit Tests</a></strong></dt>
+    <dd>Learn how to build unit tests that run on an Android device or emulator.</dd>
+</dl>
\ No newline at end of file
diff --git a/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd b/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd
new file mode 100644
index 0000000..07f0f739
--- /dev/null
+++ b/docs/html/training/testing/unit-testing/instrumented-unit-tests.jd
@@ -0,0 +1,250 @@
+page.title=Building Instrumented Unit Tests
+page.tags=testing,androidjunitrunner,junit,unit test,mock,instrumentation
+trainingnavtop=true
+
+@jd:body
+
+<!-- This is the training bar -->
+<div id="tb-wrapper">
+<div id="tb">
+  <h2>Dependencies and Prerequisites</h2>
+
+  <ul>
+    <li>Android 2.2 (API level 8) or higher</li>
+    <li><a href="{@docRoot}tools/testing-support-library/index.html">
+      Android Testing Support Library</a></li>
+  </ul>
+
+  <h2>This lesson teaches you to</h2>
+
+  <ol>
+    <li><a href="#setup">Set Up Your Testing Environment</a></li>
+    <li><a href="#build">Create a Instrumented Unit Test Class</a></li>
+    <li><a href="#run">Run Instrumented Unit Tests</a></li>
+  </ol>
+
+  <h2>Try it out</h2>
+
+  <ul>
+    <li>
+<a href="https://github.com/googlesamples/android-testing/tree/master/unittesting/BasicUnitAndroidTest"
+class="external-link">Instrumented Unit Tests Code Samples</a></li>
+  </ul>
+</div>
+</div>
+
+<p>
+Instrumented unit tests are unit tests that run on physical devices and emulators, instead of
+the Java Virtual Machine (JVM) on your local machine. You should create instrumented unit tests
+if your tests need access to instrumentation information (such as the target app's
+{@link android.content.Context}) or if they require the real implementation of an Android framework
+component (such as a {@link android.os.Parcelable} or {@link android.content.SharedPreferences}
+object). Using instrumented unit tests also helps to reduce the effort required to write and
+maintain mock code. You are still free to use a mocking framework, if you choose, to simulate any
+dependency relationships. Instrumented unit tests can take advantage of the Android framework APIs
+and supporting APIs, such as the Android Testing Support Library.
+</p>
+
+<h2 id="setup">Set Up Your Testing Environment</h2>
+<p>Before building instrumented unit tests, you must:</p>
+
+  <ul>
+      <li>
+        <strong>Install the Android Testing Support Library</strong>. The
+        <a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">
+        {@code AndroidJUnitRunner}</a> API, located under the
+        {@code com.android.support.test.runner} package, allows you to
+        create and run instrumented unit tests.  To learn how to install the
+        library, see <a href="{@docRoot}tools/testing-support-library/index.html#setup">
+        Testing Support Library Setup</a>.
+      </li>
+
+      <li>
+        <strong>Set up your project structure.</strong> In your Gradle project, the source code for
+        the target app that you want to test is typically placed under the {@code app/src/main/java}
+        folder. The source code for instrumentatation tests, including your unit tests, must be
+        placed under the <code>app/src/androidTest/java</code> folder.
+        To learn more about setting up your project directory, see
+        <a href="{@docRoot}tools/projects/index.html">Managing Projects</a>.
+      </li>
+
+      <li>
+        <strong>Specify your Android testing dependencies</strong>. In order for the
+        <a href="{@docRoot}tools/building/plugin-for-gradle.html">Android Plug-in for Gradle</a> to
+        correctly build and run your instrumented unit tests, you must specify the following
+        libraries in the {@code build.gradle} file of your Android app module:
+
+        <pre>
+dependencies {
+    androidTestCompile 'com.android.support.test:runner:0.2'
+    androidTestCompile 'com.android.support.test:rules:0.2'
+    // Set this dependency if you want to use Hamcrest matching
+    androidTestCompile 'org.hamcrest:hamcrest-library:1.1'
+}
+</pre>
+      </li>
+  </ul>
+
+<h2 id="build">Create an Instrumented Unit Test Class</h2>
+<p>
+Your instrumented unit test class should be written as a JUnit 4 test class. To learn more about
+creating JUnit 4 test classes and using JUnit 4 assertions and annotations, see
+<a href="local-unit-tests.html#build">Create a Local Unit Test Class</a>.
+</p>
+<p>To create an instrumented JUnit 4 test class, add the {@code &#64;RunWith(AndroidJUnit4.class)}
+annotation at the beginning of your test class definition. You also need to specify the
+<a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">
+{@code AndroidJUnitRunner}</a> class
+provided in the Android Testing Support Library as your default test runner. This step is described
+in more detail in <a href="#run">Run Instrumented Unit Tests</a>.
+</p>
+
+<p>The following example shows how you might write an instrumented unit test to test that
+the {@link android.os.Parcelable} interface is implemented correctly for the
+{@code LogHistory} class:</p>
+
+<pre>
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import java.util.List;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+&#64;RunWith(AndroidJUnit4.class)
+public class LogHistoryAndroidUnitTest {
+
+    public static final String TEST_STRING = "This is a string";
+    public static final long TEST_LONG = 12345678L;
+    private LogHistory mLogHistory;
+
+    &#64;Before
+    public void createLogHistory() {
+        mLogHistory = new LogHistory();
+    }
+
+    &#64;Test
+    public void logHistory_ParcelableWriteRead() {
+        // Set up the Parcelable object to send and receive.
+        mLogHistory.addEntry(TEST_STRING, TEST_LONG);
+
+        // Write the data.
+        Parcel parcel = Parcel.obtain();
+        mLogHistory.writeToParcel(parcel, mLogHistory.describeContents());
+
+        // After you're done with writing, you need to reset the parcel for reading.
+        parcel.setDataPosition(0);
+
+        // Read the data.
+        LogHistory createdFromParcel = LogHistory.CREATOR.createFromParcel(parcel);
+        List&lt;Pair&lt;String, Long&gt;&gt; createdFromParcelData = createdFromParcel.getData();
+
+        // Verify that the received data is correct.
+        assertThat(createdFromParcelData.size(), is(1));
+        assertThat(createdFromParcelData.get(0).first, is(TEST_STRING));
+        assertThat(createdFromParcelData.get(0).second, is(TEST_LONG));
+    }
+}
+</pre>
+
+<h3 id="test-suites">Creating a test suite</h3>
+<p>
+To organize the execution of your instrumented unit tests, you can group a collection of test
+classes in a <em>test suite</em> class and run these tests together. Test suites can be nested;
+your test suite can group other test suites and run all their component test classes together.
+</p>
+
+<p>
+A test suite is contained in a test package, similar to the main application package. By
+convention, the test suite package name usually ends with the {@code .suite} suffix (for example,
+{@code com.example.android.testing.mysample.suite}).
+</p>
+
+<p>
+To create a test suite for your unit tests, import the JUnit
+<a href="http://junit.sourceforge.net/javadoc/org/junit/runner/RunWith.html"
+class="external-link">{@code RunWith}</a> and
+<a href="http://junit.sourceforge.net/javadoc/org/junit/runners/Suite.html"
+class="external-link">{@code Suite}</a> classes. In your test suite, add the
+{@code &#64;RunWith(Suite.class)} and the {@code &#64;Suite.SuitClasses()} annotations. In
+the {@code &#64;Suite.SuiteClasses()} annotation, list the individual test classes or test
+suites as arguments.
+</p>
+
+<p>
+The following example shows how you might implement a test suite called {@code UnitTestSuite}
+that groups and runs the {@code CalculatorInstrumentationTest} and
+{@code CalculatorAddParameterizedTest} test classes together.
+</p>
+
+<pre>
+import com.example.android.testing.mysample.CalculatorAddParameterizedTest;
+import com.example.android.testing.mysample.CalculatorInstrumentationTest;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+// Runs all unit tests.
+&#64;RunWith(Suite.class)
+&#64;Suite.SuiteClasses({CalculatorInstrumentationTest.class,
+        CalculatorAddParameterizedTest.class})
+public class UnitTestSuite {}
+</pre>
+
+<h2 id="run">Run Instrumented Unit Tests</h2>
+<p>
+The
+<a href="https://developer.android.com/tools/building/plugin-for-gradle.html">
+  Android Plug-in for Gradle</a>
+provides a default directory ({@code src/androidTest/java}) for you to store the instrumented unit
+and integration test classes and test suites that you want to run on a device. The plug-in compiles
+the test code in that directory and then executes the test app using a test runner class. You must
+set the
+<a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">
+{@code AndroidJUnitRunner}</a> class provided in the
+<a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>
+as your default test runner.</p>
+</p>
+
+<p>To specify
+<a href="{@docRoot}reference/android/support/test/runner/AndroidJUnitRunner.html">
+{@code AndroidJUnitRunner}</a> as the default test instrumentation runner, add the following
+setting in your {@code build.gradle} file:</p>
+<pre>
+android {
+    defaultConfig {
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+}
+</pre>
+
+<h3 id="run-from-Android-Studio">Running instrumented unit tests from Android Studio</h3>
+<p>
+To run instrumented unit tests in your Gradle project from Android Studio:
+</p>
+<ol>
+<li>Open the <strong>Build Variants</strong> window by clicking the left-hand tab, then set the
+test artifact to <em>Android Instrumentation Tests</em>.
+</li>
+<li>In the <strong>Project</strong> window, drill down to your unit test class or method, then
+  right-click and run it using the Android Test configuration.
+</li>
+</ol>
+
+<p>Android Studio displays the results of the unit test execution in the <strong>Run</strong>
+window.</p>
+
+<h3 id="run-from-commandline">Running instrumented unit tests from the command-line</h3>
+
+<p>To run instrumented unit tests in your Gradle project from the command-line, call the
+  {@code connectedCheck} (or {@code cC}) task:</p>
+
+<pre>
+./gradlew cC
+</pre>
+
+<p>You can find the generated HTML test result reports in the
+{@code &lt;path_to_your_project&gt;/app/build/outputs/reports/androidTests/connected/} directory,
+and the corresponding XML files in the
+{@code &lt;path_to_your_project&gt;/app/build/outputs/androidTest-results/connected/} directory.</p>
\ No newline at end of file
diff --git a/docs/html/training/testing/unit-testing/local-unit-tests.jd b/docs/html/training/testing/unit-testing/local-unit-tests.jd
new file mode 100644
index 0000000..421709b
--- /dev/null
+++ b/docs/html/training/testing/unit-testing/local-unit-tests.jd
@@ -0,0 +1,302 @@
+page.title=Building Local Unit Tests
+page.tags=testing,androidjunitrunner,junit,unit test,mock
+trainingnavtop=true
+
+@jd:body
+
+<!-- This is the training bar -->
+<div id="tb-wrapper">
+<div id="tb">
+  <h2>Dependencies and Prerequisites</h2>
+
+  <ul>
+    <li>Android Plug-in for Gradle 1.1.0 or higher</li>
+  </ul>
+
+  <h2>This lesson teaches you to</h2>
+
+  <ol>
+    <li><a href="#setup">Set Up Your Testing Environment</a></li>
+    <li><a href="#build">Create a Local Unit Test Class</a></li>
+    <li><a href="#run">Run Local Unit Tests</a></li>
+  </ol>
+
+  <h2>Try it out</h2>
+
+  <ul>
+    <li>
+<a href="https://github.com/googlesamples/android-testing/tree/master/unittesting/BasicSample"
+class="external-link">Local Unit Tests Code Samples</a></li>
+  </ul>
+</div>
+</div>
+
+<p>If your unit test has no dependencies or only has simple dependencies on Android, you should run
+your test on a local development machine. This testing approach is efficient because it helps
+you avoid the overhead of loading the target app and unit test code onto a physical device or
+emulator every time your test is run. Consequently, the execution time for running your unit
+test is greatly reduced. With this approach, you normally use a mocking framework, like
+<a href="https://code.google.com/p/mockito/" class="external-link">Mockito</a>, to fulfill any
+dependency relationships.</p>
+
+<p><a href="{@docRoot}tools/building/plugin-for-gradle.html">Android Plug-in for Gradle</a>
+version 1.1.0 and higher allows you to create a source directory ({@code src/test/java}) in your
+project to store JUnit tests that you want to run on a local machine. This feature improves your
+project organization by letting you group your unit tests together into a single source set. You
+can run the tests from Android Studio or the command-line, and the plugin executes them on the
+local Java Virtual Machine (JVM) on your development machine. </p>
+
+<h2 id="setup">Set Up Your Testing Environment</h2>
+<p>Before building local unit tests, you must:</p>
+
+  <ul>
+      <li>
+        <strong>Set up your project structure.</strong> In your Gradle project, the source code for
+        the target app that you want to test is typically placed under the {@code app/src/main/java}
+        folder. The source code for your local unit tests must be placed under the
+        <code>app/src/test/java</code> folder.
+        To learn more about setting up your project directory, see
+        <a href="#run">Run Local Unit Tests</a> and
+        <a href="{@docRoot}tools/projects/index.html">Managing Projects</a>.
+      </li>
+
+      <li>
+        <strong>Specify your Android testing dependencies</strong>. In order to use JUnit 4 and
+        Mockito with your local unit tests, specify the following libraries in
+        the {@code build.gradle} file of your Android app module:
+
+        <pre>
+dependencies {
+    // Unit testing dependencies
+    testCompile 'junit:junit:4.12'
+    // Set this dependency if you want to use Mockito
+    testCompile 'org.mockito:mockito-core:1.10.19'
+    // Set this dependency if you want to use Hamcrest matching
+    androidTestCompile 'org.hamcrest:hamcrest-library:1.1'
+}
+</pre>
+      </li>
+  </ul>
+
+<h2 id="build">Create a Local Unit Test Class</h2>
+<p>Your local unit test class should be written as a JUnit 4 test class.
+<a href="http://junit.org/" class="external-link">JUnit</a> is the most popular
+and widely-used unit testing framework for Java. The latest version of this framework, JUnit 4,
+allows you to write tests in a cleaner and more flexible way than its predecessor versions. Unlike
+the previous approach to Android unit testing based on JUnit 3, with JUnit 4, you do not need to
+extend the {@code junit.framework.TestCase} class. You also do not need to prefix your test method
+name with the {@code ‘test’} keyword, or use any classes in the {@code junit.framework} or
+{@code junit.extensions} package.</p>
+
+<p>To create a basic JUnit 4 test class, create a Java class that contains one or more test methods.
+A test method begins with the {@code &#64;Test} annotation and contains the code to exercise
+and verify a single functionality in the component that you want to test.</p>
+
+<p>The following example shows how you might implement a local unit test class. The test method
+{@code emailValidator_CorrectEmailSimple_ReturnsTrue} verifies that the {@code isValidEmail()}
+method in the app under test returns the correct result.</p>
+
+<pre>
+import org.junit.Test;
+import java.util.regex.Pattern;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class EmailValidatorTest {
+
+    &#64;Test
+    public void emailValidator_CorrectEmailSimple_ReturnsTrue() {
+        assertThat(EmailValidator.isValidEmail("name&#64;email.com"), is(true));
+    }
+    ...
+}
+</pre>
+
+<p>To test that components in your app return the expected results, use the
+<a href="http://junit.org/javadoc/latest/org/junit/Assert.html" class="external-link">
+junit.Assert</a> methods to perform validation checks (or <em>assertions</em>) to compare the state
+of the component under test against some expected value. To make tests more readable, you
+can use <a href="https://code.google.com/p/hamcrest/wiki/Tutorial" class="external-link">
+Hamcrest matchers</a> (such as the {@code is()} and {@code equalTo()} methods) to match the
+returned result against the expected result.</p>
+
+<p>In your JUnit 4 test class, you can use annotations to call out sections in your test code for
+special processing, such as:</p>
+
+<ul>
+<li>
+{@code &#64;Before}: Use this annotation to specify a block of code with test setup operations. This
+code block will be invoked before each test. You can have multiple {@code &#64;Before} methods but
+the order which these methods are called is not fixed.
+</li>
+<li>
+{@code &#64;After}: This annotation specifies a block of code with test tear-down operations. This
+code block will be called after every test method. You can define multiple {@code &#64;After}
+operations in your test code. Use this annotation to release any resources from memory.
+</li>
+<li>
+{@code &#64;Test}: Use this annotation to mark a test method. A single test class can contain
+multiple test methods, each prefixed with this annotation.
+</li>
+<li>
+{@code &#64;BeforeClass}: Use this annotation to specify static methods to be invoked only once per
+test class. This testing step is useful for expensive operations such as connecting to a database.
+</li>
+<li>
+{@code &#64;AfterClass}: Use this annotation to specify static methods to be invoked only after all
+tests in the class have been run. This testing step is useful for releasing any resources allocated
+in the {@code &#64;BeforeClass} block.
+</li>
+<li>
+{@code &#64;Test(timeout=&lt;milliseconds&gt;)}: Specifies a timeout period for the test. If the
+test starts but does not complete within the given timeout period, it automatically fails. You must
+specify the timeout period in milliseconds, for example: {@code &#64;Test(timeout=5000)}.
+</li>
+</ul>
+
+<h3 id="mocking-dependencies">Mocking Android dependencies</h3>
+<p>
+By default, the <a href="{@docRoot}tools/building/plugin-for-gradle.html">
+Android Plug-in for Gradle</a> executes your local unit tests against a modified
+version of the {@code android.jar} library, which does not contain any actual code. Instead, method
+calls to Android classes from your unit test throw an exception.
+</p>
+<p>
+You can use a mocking framework to stub out external dependencies in your code, to easily test that
+your component interacts with a dependency in an expected way. By substituting Android dependencies
+with mock objects, you can isolate your unit test from the rest of the Android system while
+verifying that the correct methods in those dependencies are called. The
+<a href="https://code.google.com/p/mockito/" class="external-link">Mockito</a> mocking framework
+for Java (version 1.9.5 and higher) offers compatibility with Android unit testing.
+With Mockito, you can configure mock objects to return some specific value when invoked.</p>
+
+<p>To add a mock object to your local unit test using this framework, follow this programming model:
+</p>
+
+<ol>
+<li>
+Include the Mockito library dependency in your {@code build.gradle} file, as described in
+<a href="#setup">Set Up Your Testing Environment</a>.
+</li>
+<li>At the beginning of your unit test class definition, add the
+{@code &#64;RunWith(MockitoJUnitRunner.class)} annotation. This annotation tells the Mockito test
+runner to validate that your usage of the framework is correct and simplifies the initialization of
+your mock objects.
+</li>
+<li>To create a mock object for an Android dependency, add the {@code &#64;Mock} annotation before
+the field declaration.</li>
+<li>To stub the behavior of the dependency, you can specify a condition and return
+value when the condition is met by using the {@code when()} and {@code thenReturn()} methods.
+</li>
+</ol>
+
+<p>
+The following example shows how you might create a unit test that uses a mock
+{@link android.content.Context} object.
+</p>
+
+<pre>
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.CoreMatchers.*;
+import static org.mockito.Mockito.*;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import android.content.SharedPreferences;
+
+&#64;RunWith(MockitoJUnitRunner.class)
+public class UnitTestSample {
+
+    private static final String FAKE_STRING = "HELLO WORLD";
+
+    &#64;Mock
+    Context mMockContext;
+
+    &#64;Test
+    public void readStringFromContext_LocalizedString() {
+        // Given a mocked Context injected into the object under test...
+        when(mMockContext.getString(R.string.hello_word))
+                .thenReturn(FAKE_STRING);
+        ClassUnderTest myObjectUnderTest = new ClassUnderTest(mMockContext);
+
+        // ...when the string is returned from the object under test...
+        String result = myObjectUnderTest.getHelloWorldString();
+
+        // ...then the result should be the expected one.
+        assertThat(result, is(FAKE_STRING));
+    }
+}
+</pre>
+
+<p>
+To learn more about using the Mockito framework, see the
+<a href="http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html"
+class="external-link">Mockito API reference</a> and the
+{@code SharedPreferencesHelperTest} class in the
+<a href="https://github.com/googlesamples/android-testing/tree/master/unittesting/BasicSample"
+class="external-link">sample code</a>.
+</p>
+
+<h2 id="run">Run Local Unit Tests</h2>
+<p>
+The Android Plug-in for Gradle provides a default directory ({@code src/test/java}) for you to
+store unit test classes that you want to run on a local JVM. The plug-in compiles the test code in
+that directory and then executes the test app locally using the default test runner class.
+</p>
+<p>
+As with production code, you can create unit tests for a
+<a href="http://developer.android.com/tools/building/configuring-gradle.html#workBuildVariants"
+class="external-link">specific flavor or build type</a>. You should keep unit tests in a test
+source tree location that corresponds to your production source tree, such as:
+
+<table>
+<tr>
+<th>Path to Production Class</th>
+<th>Path to Local Unit Test Class</th>
+</tr>
+<tr>
+<td>{@code src/main/java/Foo.java}</td>
+<td>{@code src/test/java/FooTest.java}</td>
+</tr>
+<tr>
+<td>{@code src/debug/java/Foo.java}</td>
+<td>{@code src/testDebug/java/FooTest.java}</td>
+</tr>
+<tr>
+<td>{@code src/myFlavor/java/Foo.java}</td>
+<td>{@code src/testMyFlavor/java/FooTest.java}</td>
+</tr>
+</table>
+
+<h3 id="run-from-Android-Studio">Running local unit tests from Android Studio</h3>
+<p>
+To run local unit tests in your Gradle project from Android Studio:
+</p>
+<ol>
+<li>In the <strong>Project</strong> window, right click on the project and synchronize your project.
+</li>
+<li>Open the <strong>Build Variants</strong> window by clicking the left-hand tab, then change the
+test artifact to <em>Unit Tests</em>.
+</li>
+<li>In the <strong>Project</strong> window, drill down to your unit test class or method, then
+right-click and run it.
+</li>
+</ol>
+
+<p>Android Studio displays the results of the unit test execution in the <strong>Run</strong>
+window.</p>
+
+<h3 id="run-from-commandline">Running local unit tests from the command-line</h3>
+
+<p>To run local unit tests in your Gradle project from the command-line, call the {@code test} task
+command with the {@code --continue} option.</p>
+
+<pre>
+./gradlew test --continue
+</pre>
+
+<p>If there are failing tests, the command will display links to HTML reports (one per build
+variant). You can find the generated HTML test result reports in the
+{@code &lt;path_to_your_project&gt;/app/build/reports/tests/} directory, and the corresponding XML
+files in the {@code &lt;path_to_your_project&gt;/app/build/test-results/} directory.</p>
\ No newline at end of file
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 862663e..089739b 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -1875,6 +1875,24 @@
         </ul>
       </li>
     </ul>
+    <ul>
+      <li class="nav-section">
+      <div class="nav-section-header"><a href="<?cs var:toroot ?>training/testing/unit-testing/index.html"
+         description="How to build effective unit tests for Android apps.">
+            Building Effective Unit Tests
+          </a></div>
+        <ul>
+          <li><a href="<?cs var:toroot ?>training/testing/unit-testing/local-unit-tests.html">
+            <span class="en">Building Local Unit Tests</span>
+            </a>
+          </li>
+          <li><a href="<?cs var:toroot ?>training/testing/unit-testing/instrumented-unit-tests.html">
+            <span class="en">Building Instrumented Unit Tests</span>
+            </a>
+          </li>
+        </ul>
+      </li>
+    </ul>
   </li>
   <!-- End best Testing -->
 
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 1324311..2d8b0b2 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -1558,7 +1558,10 @@
      *  If other is null, return false.
      */
     public boolean sameAs(Bitmap other) {
-        checkRecycled("Can't sameAs on a recycled bitmap!");
+        checkRecycled("Can't call sameAs on a recycled bitmap!");
+        if (other.isRecycled()) {
+            throw new IllegalArgumentException("Can't compare to a recycled bitmap!");
+        }
         return this == other || (other != null
                 && nativeSameAs(mFinalizer.mNativeBitmap, other.mFinalizer.mNativeBitmap));
     }
diff --git a/keystore/java/android/security/EcIesParameterSpec.java b/keystore/java/android/security/EcIesParameterSpec.java
index a3e5aec..af93519 100644
--- a/keystore/java/android/security/EcIesParameterSpec.java
+++ b/keystore/java/android/security/EcIesParameterSpec.java
@@ -1,6 +1,8 @@
 package android.security;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -50,7 +52,11 @@
 public class EcIesParameterSpec implements AlgorithmParameterSpec {
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(value = {PointFormat.UNCOMPRESSED, PointFormat.COMPRESSED})
+    @IntDef({
+        PointFormat.UNSPECIFIED,
+        PointFormat.UNCOMPRESSED,
+        PointFormat.COMPRESSED,
+        })
     public @interface PointFormatEnum {}
 
     /**
@@ -127,6 +133,7 @@
      * Returns KEM KDF algorithm (e.g., {@code HKDFwithSHA256} or {@code KDF1withSHA1}) or
      * {@code null} if not specified.
      */
+    @Nullable
     public String getKemKdfAlgorithm() {
         return mKemKdfAlgorithm;
     }
@@ -138,6 +145,7 @@
      * @see Cipher#getInstance(String)
      * @see #getDemCipherKeySize()
      */
+    @Nullable
     public String getDemCipherTransformation() {
         return mDemCipherTransformation;
     }
@@ -158,6 +166,7 @@
      * @see Mac#getInstance(String)
      * @see #getDemMacKeySize()
      */
+    @Nullable
     public String getDemMacAlgorithm() {
         return mDemMacAlgorithm;
     }
@@ -194,7 +203,8 @@
          * Sets KEM KDF algorithm. For example, {@code HKDFwithSHA256}, {@code KDF2withSHA256}, or
          * {@code KDF1withSHA1}.
          */
-        public Builder setKemKdfAlgorithm(String algorithm) {
+        @NonNull
+        public Builder setKemKdfAlgorithm(@Nullable String algorithm) {
             mKemKdfAlgorithm = algorithm;
             return this;
         }
@@ -205,7 +215,8 @@
          *
          * @see Cipher#getInstance(String)
          */
-        public Builder setDemCipherTransformation(String transformation) {
+        @NonNull
+        public Builder setDemCipherTransformation(@Nullable String transformation) {
             mDemCipherTransformation = transformation;
             return this;
         }
@@ -217,6 +228,7 @@
          *
          * @see #setDemCipherTransformation(String)
          */
+        @NonNull
         public Builder setDemCipherKeySize(int sizeBits) {
             mDemCipherKeySize = sizeBits;
             return this;
@@ -227,7 +239,8 @@
          *
          * @see Mac#getInstance(String)
          */
-        public Builder setDemMacAlgorithm(String algorithm) {
+        @NonNull
+        public Builder setDemMacAlgorithm(@Nullable String algorithm) {
             mDemMacAlgorithm = algorithm;
             return this;
         }
@@ -239,6 +252,7 @@
          *
          * @see #setDemCipherKeySize(int)
          */
+        @NonNull
         public Builder setDemMacKeySize(int sizeBits) {
             mDemMacKeySize = sizeBits;
             return this;
@@ -247,6 +261,7 @@
         /**
          * Returns a new {@link EcIesParameterSpec} based on the current state of this builder.
          */
+        @NonNull
         public EcIesParameterSpec build() {
             int demMacKeySize = (mDemMacKeySize != -1) ? mDemMacKeySize : mDemCipherKeySize;
             return new EcIesParameterSpec(
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 8e27dc3..d3dbebf 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -15,6 +15,8 @@
  */
 package android.security;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -217,6 +219,7 @@
      * successfully installed, otherwise {@link
      * Activity#RESULT_CANCELED} will be returned.
      */
+    @NonNull
     public static Intent createInstallIntent() {
         Intent intent = new Intent(ACTION_INSTALL);
         intent.setClassName(CERT_INSTALLER_PACKAGE,
@@ -261,9 +264,10 @@
      * @param alias The alias to preselect if available, or null if
      *     unavailable.
      */
-    public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasCallback response,
+    public static void choosePrivateKeyAlias(@NonNull Activity activity,
+            @NonNull KeyChainAliasCallback response,
             @KeyStoreKeyProperties.AlgorithmEnum String[] keyTypes, Principal[] issuers,
-            String host, int port, String alias) {
+            @Nullable String host, int port, @Nullable String alias) {
         choosePrivateKeyAlias(activity, response, keyTypes, issuers, host, port, null, alias);
     }
 
@@ -306,9 +310,10 @@
      * @param alias The alias to preselect if available, or null if
      *     unavailable.
      */
-    public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasCallback response,
+    public static void choosePrivateKeyAlias(@NonNull Activity activity,
+            @NonNull KeyChainAliasCallback response,
             @KeyStoreKeyProperties.AlgorithmEnum String[] keyTypes, Principal[] issuers,
-            String host, int port, String url, String alias) {
+            @Nullable String host, int port, @Nullable String url, @Nullable String alias) {
         /*
          * TODO currently keyTypes, issuers are unused. They are meant
          * to follow the semantics and purpose of X509KeyManager
@@ -361,7 +366,8 @@
      * returned via {@link KeyChainAliasCallback#alias}.
      * @throws KeyChainException if the alias was valid but there was some problem accessing it.
      */
-    public static PrivateKey getPrivateKey(Context context, String alias)
+    @Nullable
+    public static PrivateKey getPrivateKey(@NonNull Context context, @NonNull String alias)
             throws KeyChainException, InterruptedException {
         if (alias == null) {
             throw new NullPointerException("alias == null");
@@ -396,8 +402,9 @@
      * returned via {@link KeyChainAliasCallback#alias}.
      * @throws KeyChainException if the alias was valid but there was some problem accessing it.
      */
-    public static X509Certificate[] getCertificateChain(Context context, String alias)
-            throws KeyChainException, InterruptedException {
+    @Nullable
+    public static X509Certificate[] getCertificateChain(@NonNull Context context,
+            @NonNull String alias) throws KeyChainException, InterruptedException {
         if (alias == null) {
             throw new NullPointerException("alias == null");
         }
@@ -432,7 +439,7 @@
      * "RSA").
      */
     public static boolean isKeyAlgorithmSupported(
-            @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
+            @NonNull @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
         final String algUpper = algorithm.toUpperCase(Locale.US);
         return KeyStoreKeyProperties.Algorithm.EC.equals(algUpper)
                 || KeyStoreKeyProperties.Algorithm.RSA.equals(algUpper);
@@ -446,7 +453,7 @@
      * that makes it non-exportable.
      */
     public static boolean isBoundKeyAlgorithm(
-            @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
+            @NonNull @KeyStoreKeyProperties.AlgorithmEnum String algorithm) {
         if (!isKeyAlgorithmSupported(algorithm)) {
             return false;
         }
@@ -455,7 +462,8 @@
     }
 
     /** @hide */
-    public static X509Certificate toCertificate(byte[] bytes) {
+    @NonNull
+    public static X509Certificate toCertificate(@NonNull byte[] bytes) {
         if (bytes == null) {
             throw new IllegalArgumentException("bytes == null");
         }
@@ -496,14 +504,14 @@
      *
      * Caller should call unbindService on the result when finished.
      */
-    public static KeyChainConnection bind(Context context) throws InterruptedException {
+    public static KeyChainConnection bind(@NonNull Context context) throws InterruptedException {
         return bindAsUser(context, Process.myUserHandle());
     }
 
     /**
      * @hide
      */
-    public static KeyChainConnection bindAsUser(Context context, UserHandle user)
+    public static KeyChainConnection bindAsUser(@NonNull Context context, UserHandle user)
             throws InterruptedException {
         if (context == null) {
             throw new NullPointerException("context == null");
@@ -537,7 +545,7 @@
         return new KeyChainConnection(context, keyChainServiceConnection, q.take());
     }
 
-    private static void ensureNotOnMainThread(Context context) {
+    private static void ensureNotOnMainThread(@NonNull Context context) {
         Looper looper = Looper.myLooper();
         if (looper != null && looper == context.getMainLooper()) {
             throw new IllegalStateException(
diff --git a/keystore/java/android/security/KeyChainAliasCallback.java b/keystore/java/android/security/KeyChainAliasCallback.java
index 2500863..8e41377 100644
--- a/keystore/java/android/security/KeyChainAliasCallback.java
+++ b/keystore/java/android/security/KeyChainAliasCallback.java
@@ -15,6 +15,8 @@
  */
 package android.security;
 
+import android.annotation.Nullable;
+
 /**
  * The KeyChainAliasCallback is the callback for {@link
  * KeyChain#choosePrivateKeyAlias}.
@@ -25,5 +27,5 @@
      * Called with the alias of the certificate chosen by the user, or
      * null if no value was chosen.
      */
-    public void alias(String alias);
+    public void alias(@Nullable String alias);
 }
diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java
index 97e3a67..404f939 100644
--- a/keystore/java/android/security/KeyGeneratorSpec.java
+++ b/keystore/java/android/security/KeyGeneratorSpec.java
@@ -16,6 +16,9 @@
 
 package android.security;
 
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.KeyguardManager;
 import android.content.Context;
 import android.text.TextUtils;
@@ -163,6 +166,7 @@
      *
      * @return instant or {@code null} if not restricted.
      */
+    @Nullable
     public Date getKeyValidityStart() {
         return mKeyValidityStart;
     }
@@ -172,6 +176,7 @@
      *
      * @return instant or {@code null} if not restricted.
      */
+    @Nullable
     public Date getKeyValidityForConsumptionEnd() {
         return mKeyValidityForConsumptionEnd;
     }
@@ -181,6 +186,7 @@
      *
      * @return instant or {@code null} if not restricted.
      */
+    @Nullable
     public Date getKeyValidityForOriginationEnd() {
         return mKeyValidityForOriginationEnd;
     }
@@ -195,6 +201,7 @@
     /**
      * Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
      */
+    @NonNull
     public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
         return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
     }
@@ -202,6 +209,7 @@
     /**
      * Gets the set of block modes with which the key can be used.
      */
+    @NonNull
     public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
         return ArrayUtils.cloneIfNotEmpty(mBlockModes);
     }
@@ -269,7 +277,7 @@
          * {@code context} passed in may be used to pop up some UI to ask the user to unlock or
          * initialize the Android KeyStore facility.
          */
-        public Builder(Context context) {
+        public Builder(@NonNull Context context) {
             if (context == null) {
                 throw new NullPointerException("context == null");
             }
@@ -282,7 +290,8 @@
          *
          * <p>The alias must be provided. There is no default.
          */
-        public Builder setAlias(String alias) {
+        @NonNull
+        public Builder setAlias(@NonNull String alias) {
             if (alias == null) {
                 throw new NullPointerException("alias == null");
             }
@@ -296,6 +305,7 @@
          * <p>By default, the key size will be determines based on the key algorithm. For example,
          * for {@code HmacSHA256}, the key size will default to {@code 256}.
          */
+        @NonNull
         public Builder setKeySize(int keySize) {
             mKeySize = keySize;
             return this;
@@ -313,6 +323,7 @@
          *
          * @see KeyguardManager#isDeviceSecure()
          */
+        @NonNull
         public Builder setEncryptionRequired() {
             mFlags |= KeyStore.FLAG_ENCRYPTED;
             return this;
@@ -325,6 +336,7 @@
          *
          * @see #setKeyValidityEnd(Date)
          */
+        @NonNull
         public Builder setKeyValidityStart(Date startDate) {
             mKeyValidityStart = startDate;
             return this;
@@ -339,6 +351,7 @@
          * @see #setKeyValidityForConsumptionEnd(Date)
          * @see #setKeyValidityForOriginationEnd(Date)
          */
+        @NonNull
         public Builder setKeyValidityEnd(Date endDate) {
             setKeyValidityForOriginationEnd(endDate);
             setKeyValidityForConsumptionEnd(endDate);
@@ -352,6 +365,7 @@
          *
          * @see #setKeyValidityForConsumptionEnd(Date)
          */
+        @NonNull
         public Builder setKeyValidityForOriginationEnd(Date endDate) {
             mKeyValidityForOriginationEnd = endDate;
             return this;
@@ -365,6 +379,7 @@
          *
          * @see #setKeyValidityForOriginationEnd(Date)
          */
+        @NonNull
         public Builder setKeyValidityForConsumptionEnd(Date endDate) {
             mKeyValidityForConsumptionEnd = endDate;
             return this;
@@ -375,6 +390,7 @@
          *
          * <p>This must be specified for all keys. There is no default.
          */
+        @NonNull
         public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) {
             mPurposes = purposes;
             return this;
@@ -387,6 +403,7 @@
          *
          * <p>This must be specified for keys which are used for encryption/decryption.
          */
+        @NonNull
         public Builder setEncryptionPaddings(
                 @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
             mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
@@ -399,6 +416,7 @@
          *
          * <p>This must be specified for encryption/decryption keys.
          */
+        @NonNull
         public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
             mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
             return this;
@@ -436,6 +454,7 @@
          * ciphertext.</li>
          * </ul>
          */
+        @NonNull
         public Builder setRandomizedEncryptionRequired(boolean required) {
             mRandomizedEncryptionRequired = required;
             return this;
@@ -456,6 +475,7 @@
          *
          * @see #setUserAuthenticationValidityDurationSeconds(int)
          */
+        @NonNull
         public Builder setUserAuthenticationRequired(boolean required) {
             mUserAuthenticationRequired = required;
             return this;
@@ -472,7 +492,9 @@
          *
          * @see #setUserAuthenticationRequired(boolean)
          */
-        public Builder setUserAuthenticationValidityDurationSeconds(int seconds) {
+        @NonNull
+        public Builder setUserAuthenticationValidityDurationSeconds(
+                @IntRange(from = -1) int seconds) {
             mUserAuthenticationValidityDurationSeconds = seconds;
             return this;
         }
@@ -482,6 +504,7 @@
          *
          * @throws IllegalArgumentException if a required field is missing or violates a constraint.
          */
+        @NonNull
         public KeyGeneratorSpec build() {
             return new KeyGeneratorSpec(mContext,
                     mKeystoreAlias,
diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java
index 7fd5cb5..2086ccb 100644
--- a/keystore/java/android/security/KeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/KeyPairGeneratorSpec.java
@@ -17,6 +17,9 @@
 package android.security;
 
 import android.app.KeyguardManager;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.text.TextUtils;
 
@@ -286,6 +289,7 @@
     /**
      * Returns the key type (e.g., "EC", "RSA") specified by this parameter.
      */
+    @Nullable
     public @KeyStoreKeyProperties.AlgorithmEnum String getKeyType() {
         return mKeyType;
     }
@@ -303,6 +307,7 @@
      * Returns the {@link AlgorithmParameterSpec} that will be used for creation
      * of the key pair.
      */
+    @NonNull
     public AlgorithmParameterSpec getAlgorithmParameterSpec() {
         return mSpec;
     }
@@ -311,6 +316,7 @@
      * Gets the subject distinguished name to be used on the X.509 certificate
      * that will be put in the {@link java.security.KeyStore}.
      */
+    @NonNull
     public X500Principal getSubjectDN() {
         return mSubjectDN;
     }
@@ -319,6 +325,7 @@
      * Gets the serial number to be used on the X.509 certificate that will be
      * put in the {@link java.security.KeyStore}.
      */
+    @NonNull
     public BigInteger getSerialNumber() {
         return mSerialNumber;
     }
@@ -327,6 +334,7 @@
      * Gets the start date to be used on the X.509 certificate that will be put
      * in the {@link java.security.KeyStore}.
      */
+    @NonNull
     public Date getStartDate() {
         return mStartDate;
     }
@@ -335,6 +343,7 @@
      * Gets the end date to be used on the X.509 certificate that will be put in
      * the {@link java.security.KeyStore}.
      */
+    @NonNull
     public Date getEndDate() {
         return mEndDate;
     }
@@ -359,6 +368,7 @@
      *
      * @return instant or {@code null} if not restricted.
      */
+    @Nullable
     public Date getKeyValidityStart() {
         return mKeyValidityStart;
     }
@@ -369,6 +379,7 @@
      *
      * @return instant or {@code null} if not restricted.
      */
+    @Nullable
     public Date getKeyValidityForConsumptionEnd() {
         return mKeyValidityForConsumptionEnd;
     }
@@ -378,6 +389,7 @@
      *
      * @return instant or {@code null} if not restricted.
      */
+    @Nullable
     public Date getKeyValidityForOriginationEnd() {
         return mKeyValidityForOriginationEnd;
     }
@@ -392,6 +404,7 @@
     /**
      * Gets the set of digest algorithms with which the key can be used.
      */
+    @NonNull
     public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
         return ArrayUtils.cloneIfNotEmpty(mDigests);
     }
@@ -399,6 +412,7 @@
     /**
      * Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
      */
+    @NonNull
     public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
         return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
     }
@@ -406,6 +420,7 @@
     /**
      * Gets the set of padding schemes with which the key can be used when signing/verifying.
      */
+    @NonNull
     public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
         return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
     }
@@ -413,6 +428,7 @@
     /**
      * Gets the set of block modes with which the key can be used.
      */
+    @NonNull
     public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
         return ArrayUtils.cloneIfNotEmpty(mBlockModes);
     }
@@ -528,7 +544,7 @@
          * some UI to ask the user to unlock or initialize the Android KeyStore
          * facility.
          */
-        public Builder(Context context) {
+        public Builder(@NonNull Context context) {
             if (context == null) {
                 throw new NullPointerException("context == null");
             }
@@ -540,7 +556,8 @@
          * {@link java.security.KeyStore} instance using the
          * {@code AndroidKeyStore} provider.
          */
-        public Builder setAlias(String alias) {
+        @NonNull
+        public Builder setAlias(@NonNull String alias) {
             if (alias == null) {
                 throw new NullPointerException("alias == null");
             }
@@ -551,7 +568,8 @@
         /**
          * Sets the key type (e.g., EC, RSA) of the keypair to be created.
          */
-        public Builder setKeyType(@KeyStoreKeyProperties.AlgorithmEnum String keyType)
+        @NonNull
+        public Builder setKeyType(@NonNull @KeyStoreKeyProperties.AlgorithmEnum String keyType)
                 throws NoSuchAlgorithmException {
             if (keyType == null) {
                 throw new NullPointerException("keyType == null");
@@ -569,6 +587,7 @@
          * key type of RSA this will set the modulus size and for a key type of
          * EC it will select a curve with a matching field size.
          */
+        @NonNull
         public Builder setKeySize(int keySize) {
             if (keySize < 0) {
                 throw new IllegalArgumentException("keySize < 0");
@@ -581,7 +600,7 @@
          * Sets the algorithm-specific key generation parameters. For example, for RSA keys
          * this may be an instance of {@link java.security.spec.RSAKeyGenParameterSpec}.
          */
-        public Builder setAlgorithmParameterSpec(AlgorithmParameterSpec spec) {
+        public Builder setAlgorithmParameterSpec(@NonNull AlgorithmParameterSpec spec) {
             if (spec == null) {
                 throw new NullPointerException("spec == null");
             }
@@ -597,7 +616,8 @@
          * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On
          * newer platforms the subject defaults to {@code CN=fake} if not specified.
          */
-        public Builder setSubject(X500Principal subject) {
+        @NonNull
+        public Builder setSubject(@NonNull X500Principal subject) {
             if (subject == null) {
                 throw new NullPointerException("subject == null");
             }
@@ -613,7 +633,8 @@
          * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On
          * newer platforms the serial number defaults to {@code 1} if not specified.
          */
-        public Builder setSerialNumber(BigInteger serialNumber) {
+        @NonNull
+        public Builder setSerialNumber(@NonNull BigInteger serialNumber) {
             if (serialNumber == null) {
                 throw new NullPointerException("serialNumber == null");
             }
@@ -629,7 +650,8 @@
          * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On
          * newer platforms the date defaults to {@code Jan 1 1970} if not specified.
          */
-        public Builder setStartDate(Date startDate) {
+        @NonNull
+        public Builder setStartDate(@NonNull Date startDate) {
             if (startDate == null) {
                 throw new NullPointerException("startDate == null");
             }
@@ -645,7 +667,8 @@
          * {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1 LOLLIPOP_MR1} and older platforms. On
          * newer platforms the date defaults to {@code Jan 1 2048} if not specified.
          */
-        public Builder setEndDate(Date endDate) {
+        @NonNull
+        public Builder setEndDate(@NonNull Date endDate) {
             if (endDate == null) {
                 throw new NullPointerException("endDate == null");
             }
@@ -665,6 +688,7 @@
          *
          * @see KeyguardManager#isDeviceSecure()
          */
+        @NonNull
         public Builder setEncryptionRequired() {
             mFlags |= KeyStore.FLAG_ENCRYPTED;
             return this;
@@ -679,6 +703,7 @@
          *
          * @see #setKeyValidityEnd(Date)
          */
+        @NonNull
         public Builder setKeyValidityStart(Date startDate) {
             mKeyValidityStart = startDate;
             return this;
@@ -695,6 +720,7 @@
          * @see #setKeyValidityForConsumptionEnd(Date)
          * @see #setKeyValidityForOriginationEnd(Date)
          */
+        @NonNull
         public Builder setKeyValidityEnd(Date endDate) {
             setKeyValidityForOriginationEnd(endDate);
             setKeyValidityForConsumptionEnd(endDate);
@@ -710,6 +736,7 @@
          *
          * @see #setKeyValidityForConsumptionEnd(Date)
          */
+        @NonNull
         public Builder setKeyValidityForOriginationEnd(Date endDate) {
             mKeyValidityForOriginationEnd = endDate;
             return this;
@@ -725,6 +752,7 @@
          *
          * @see #setKeyValidityForOriginationEnd(Date)
          */
+        @NonNull
         public Builder setKeyValidityForConsumptionEnd(Date endDate) {
             mKeyValidityForConsumptionEnd = endDate;
             return this;
@@ -743,6 +771,7 @@
          *
          * <p><b>NOTE: This has currently no effect.
          */
+        @NonNull
         public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) {
             mPurposes = purposes;
             return this;
@@ -756,6 +785,7 @@
          *
          * <p><b>NOTE: This has currently no effect.
          */
+        @NonNull
         public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) {
             mDigests = ArrayUtils.cloneIfNotEmpty(digests);
             return this;
@@ -770,6 +800,7 @@
          *
          * <p><b>NOTE: This has currently no effect.
          */
+        @NonNull
         public Builder setEncryptionPaddings(
                 @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
             mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
@@ -785,6 +816,7 @@
          *
          * <p><b>NOTE: This has currently no effect.
          */
+        @NonNull
         public Builder setSignaturePaddings(
                 @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) {
             mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings);
@@ -799,6 +831,7 @@
          *
          * <p><b>NOTE: This has currently no effect.
          */
+        @NonNull
         public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
             mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
             return this;
@@ -826,6 +859,7 @@
          *
          * <p><b>NOTE: This has currently no effect.
          */
+        @NonNull
         public Builder setRandomizedEncryptionRequired(boolean required) {
             mRandomizedEncryptionRequired = required;
             return this;
@@ -851,6 +885,7 @@
          *
          * @see #setUserAuthenticationValidityDurationSeconds(int)
          */
+        @NonNull
         public Builder setUserAuthenticationRequired(boolean required) {
             mUserAuthenticationRequired = required;
             return this;
@@ -872,7 +907,9 @@
          *
          * @see #setUserAuthenticationRequired(boolean)
          */
-        public Builder setUserAuthenticationValidityDurationSeconds(int seconds) {
+        @NonNull
+        public Builder setUserAuthenticationValidityDurationSeconds(
+                @IntRange(from = -1) int seconds) {
             mUserAuthenticationValidityDurationSeconds = seconds;
             return this;
         }
@@ -883,6 +920,7 @@
          * @throws IllegalArgumentException if a required field is missing
          * @return built instance of {@code KeyPairGeneratorSpec}
          */
+        @NonNull
         public KeyPairGeneratorSpec build() {
             return new KeyPairGeneratorSpec(mContext,
                     mKeystoreAlias,
diff --git a/keystore/java/android/security/KeyStoreKeyProperties.java b/keystore/java/android/security/KeyStoreKeyProperties.java
index 1cf6a7a..d1b0e5b 100644
--- a/keystore/java/android/security/KeyStoreKeyProperties.java
+++ b/keystore/java/android/security/KeyStoreKeyProperties.java
@@ -17,6 +17,8 @@
 package android.security;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.StringDef;
 import android.security.keymaster.KeymasterDefs;
 
@@ -111,6 +113,7 @@
         /**
          * @hide
          */
+        @NonNull
         public static int[] allToKeymaster(@PurposeEnum int purposes) {
             int[] result = getSetFlags(purposes);
             for (int i = 0; i < result.length; i++) {
@@ -122,7 +125,7 @@
         /**
          * @hide
          */
-        public static @PurposeEnum int allFromKeymaster(Collection<Integer> purposes) {
+        public static @PurposeEnum int allFromKeymaster(@NonNull Collection<Integer> purposes) {
             @PurposeEnum int result = 0;
             for (int keymasterPurpose : purposes) {
                 result |= fromKeymaster(keymasterPurpose);
@@ -182,7 +185,7 @@
         /**
          * @hide
          */
-        static int toKeymasterSecretKeyAlgorithm(@AlgorithmEnum String algorithm) {
+        static int toKeymasterSecretKeyAlgorithm(@NonNull @AlgorithmEnum String algorithm) {
             if (AES.equalsIgnoreCase(algorithm)) {
                 return KeymasterDefs.KM_ALGORITHM_AES;
             } else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
@@ -196,6 +199,7 @@
         /**
          * @hide
          */
+        @NonNull
         static @AlgorithmEnum String fromKeymasterSecretKeyAlgorithm(
                 int keymasterAlgorithm, int keymasterDigest) {
             switch (keymasterAlgorithm) {
@@ -232,7 +236,7 @@
          *
          * @return keymaster digest or {@code -1} if the algorithm does not involve a digest.
          */
-        static int toKeymasterDigest(@AlgorithmEnum String algorithm) {
+        static int toKeymasterDigest(@NonNull @AlgorithmEnum String algorithm) {
             String algorithmUpper = algorithm.toUpperCase(Locale.US);
             if (algorithmUpper.startsWith("HMAC")) {
                 String digestUpper = algorithmUpper.substring("HMAC".length());
@@ -287,7 +291,7 @@
         /**
          * @hide
          */
-        static int toKeymaster(@BlockModeEnum String blockMode) {
+        static int toKeymaster(@NonNull @BlockModeEnum String blockMode) {
             if (ECB.equalsIgnoreCase(blockMode)) {
                 return KeymasterDefs.KM_MODE_ECB;
             } else if (CBC.equalsIgnoreCase(blockMode)) {
@@ -304,6 +308,7 @@
         /**
          * @hide
          */
+        @NonNull
         static @BlockModeEnum String fromKeymaster(int blockMode) {
             switch (blockMode) {
                 case KeymasterDefs.KM_MODE_ECB:
@@ -322,7 +327,8 @@
         /**
          * @hide
          */
-        static @BlockModeEnum String[] allFromKeymaster(Collection<Integer> blockModes) {
+        @NonNull
+        static @BlockModeEnum String[] allFromKeymaster(@NonNull Collection<Integer> blockModes) {
             if ((blockModes == null) || (blockModes.isEmpty())) {
                 return EmptyArray.STRING;
             }
@@ -338,7 +344,7 @@
         /**
          * @hide
          */
-        static int[] allToKeymaster(@BlockModeEnum String[] blockModes) {
+        static int[] allToKeymaster(@Nullable @BlockModeEnum String[] blockModes) {
             if ((blockModes == null) || (blockModes.length == 0)) {
                 return EmptyArray.INT;
             }
@@ -388,7 +394,7 @@
         /**
          * @hide
          */
-        static int toKeymaster(@EncryptionPaddingEnum String padding) {
+        static int toKeymaster(@NonNull @EncryptionPaddingEnum String padding) {
             if (NONE.equalsIgnoreCase(padding)) {
                 return KeymasterDefs.KM_PAD_NONE;
             } else if (PKCS7.equalsIgnoreCase(padding)) {
@@ -406,6 +412,7 @@
         /**
          * @hide
          */
+        @NonNull
         static @EncryptionPaddingEnum String fromKeymaster(int padding) {
             switch (padding) {
                 case KeymasterDefs.KM_PAD_NONE:
@@ -425,7 +432,8 @@
         /**
          * @hide
          */
-        static int[] allToKeymaster(@EncryptionPaddingEnum String[] paddings) {
+        @NonNull
+        static int[] allToKeymaster(@Nullable @EncryptionPaddingEnum String[] paddings) {
             if ((paddings == null) || (paddings.length == 0)) {
                 return EmptyArray.INT;
             }
@@ -463,7 +471,7 @@
         /**
          * @hide
          */
-        static int toKeymaster(@SignaturePaddingEnum String padding) {
+        static int toKeymaster(@NonNull @SignaturePaddingEnum String padding) {
             switch (padding.toUpperCase(Locale.US)) {
                 case RSA_PKCS1:
                     return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
@@ -478,6 +486,7 @@
         /**
          * @hide
          */
+        @NonNull
         static @SignaturePaddingEnum String fromKeymaster(int padding) {
             switch (padding) {
                 case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
@@ -492,7 +501,8 @@
         /**
          * @hide
          */
-        static int[] allToKeymaster(@SignaturePaddingEnum String[] paddings) {
+        @NonNull
+        static int[] allToKeymaster(@Nullable @SignaturePaddingEnum String[] paddings) {
             if ((paddings == null) || (paddings.length == 0)) {
                 return EmptyArray.INT;
             }
@@ -561,7 +571,7 @@
         /**
          * @hide
          */
-        static int toKeymaster(@DigestEnum String digest) {
+        static int toKeymaster(@NonNull @DigestEnum String digest) {
             switch (digest.toUpperCase(Locale.US)) {
                 case SHA1:
                     return KeymasterDefs.KM_DIGEST_SHA1;
@@ -585,6 +595,7 @@
         /**
          * @hide
          */
+        @NonNull
         static @DigestEnum String fromKeymaster(int digest) {
             switch (digest) {
                 case KeymasterDefs.KM_DIGEST_NONE:
@@ -609,7 +620,8 @@
         /**
          * @hide
          */
-        static @DigestEnum String[] allFromKeymaster(Collection<Integer> digests) {
+        @NonNull
+        static @DigestEnum String[] allFromKeymaster(@NonNull Collection<Integer> digests) {
             if (digests.isEmpty()) {
                 return EmptyArray.STRING;
             }
@@ -625,7 +637,8 @@
         /**
          * @hide
          */
-        static int[] allToKeymaster(@DigestEnum String[] digests) {
+        @NonNull
+        static int[] allToKeymaster(@Nullable @DigestEnum String[] digests) {
             if ((digests == null) || (digests.length == 0)) {
                 return EmptyArray.INT;
             }
diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java
index 0a9acbb..81a19bb 100644
--- a/keystore/java/android/security/KeyStoreKeySpec.java
+++ b/keystore/java/android/security/KeyStoreKeySpec.java
@@ -16,6 +16,9 @@
 
 package android.security;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
 import java.security.PrivateKey;
 import java.security.spec.KeySpec;
 import java.util.Date;
@@ -150,6 +153,7 @@
      *
      * @return instant or {@code null} if not restricted.
      */
+    @Nullable
     public Date getKeyValidityStart() {
         return mKeyValidityStart;
     }
@@ -159,6 +163,7 @@
      *
      * @return instant or {@code null} if not restricted.
      */
+    @Nullable
     public Date getKeyValidityForConsumptionEnd() {
         return mKeyValidityForConsumptionEnd;
     }
@@ -168,6 +173,7 @@
      *
      * @return instant or {@code null} if not restricted.
      */
+    @Nullable
     public Date getKeyValidityForOriginationEnd() {
         return mKeyValidityForOriginationEnd;
     }
@@ -182,6 +188,7 @@
     /**
      * Gets the set of block modes with which the key can be used.
      */
+    @NonNull
     public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
         return ArrayUtils.cloneIfNotEmpty(mBlockModes);
     }
@@ -189,6 +196,7 @@
     /**
      * Gets the set of padding modes with which the key can be used when encrypting/decrypting.
      */
+    @NonNull
     public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
         return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
     }
@@ -196,6 +204,7 @@
     /**
      * Gets the set of padding modes with which the key can be used when signing/verifying.
      */
+    @NonNull
     public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
         return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
     }
@@ -203,6 +212,7 @@
     /**
      * Gets the set of digest algorithms with which the key can be used.
      */
+    @NonNull
     public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
         return ArrayUtils.cloneIfNotEmpty(mDigests);
     }
diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java
index 7332332..4a736c3 100644
--- a/keystore/java/android/security/KeyStoreParameter.java
+++ b/keystore/java/android/security/KeyStoreParameter.java
@@ -16,6 +16,9 @@
 
 package android.security;
 
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.KeyguardManager;
 import android.content.Context;
 
@@ -182,6 +185,7 @@
      *
      * @return instant or {@code null} if not restricted.
      */
+    @Nullable
     public Date getKeyValidityStart() {
         return mKeyValidityStart;
     }
@@ -191,6 +195,7 @@
      *
      * @return instant or {@code null} if not restricted.
      */
+    @Nullable
     public Date getKeyValidityForConsumptionEnd() {
         return mKeyValidityForConsumptionEnd;
     }
@@ -200,6 +205,7 @@
      *
      * @return instant or {@code null} if not restricted.
      */
+    @Nullable
     public Date getKeyValidityForOriginationEnd() {
         return mKeyValidityForOriginationEnd;
     }
@@ -214,6 +220,7 @@
     /**
      * Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
      */
+    @NonNull
     public @KeyStoreKeyProperties.EncryptionPaddingEnum String[] getEncryptionPaddings() {
         return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
     }
@@ -222,6 +229,7 @@
      * Gets the set of padding schemes with which the key can be used when signing or verifying
      * signatures.
      */
+    @NonNull
     public @KeyStoreKeyProperties.SignaturePaddingEnum String[] getSignaturePaddings() {
         return ArrayUtils.cloneIfNotEmpty(mSignaturePaddings);
     }
@@ -233,6 +241,7 @@
      *
      * @see #isDigestsSpecified()
      */
+    @NonNull
     public @KeyStoreKeyProperties.DigestEnum String[] getDigests() {
         if (mDigests == null) {
             throw new IllegalStateException("Digests not specified");
@@ -246,6 +255,7 @@
      *
      * @see #getDigests()
      */
+    @NonNull
     public boolean isDigestsSpecified() {
         return mDigests != null;
     }
@@ -253,6 +263,7 @@
     /**
      * Gets the set of block modes with which the key can be used.
      */
+    @NonNull
     public @KeyStoreKeyProperties.BlockModeEnum String[] getBlockModes() {
         return ArrayUtils.cloneIfNotEmpty(mBlockModes);
     }
@@ -330,7 +341,7 @@
          * some UI to ask the user to unlock or initialize the Android KeyStore
          * facility.
          */
-        public Builder(Context context) {
+        public Builder(@NonNull Context context) {
             if (context == null) {
                 throw new NullPointerException("context == null");
             }
@@ -350,6 +361,7 @@
          *
          * @see KeyguardManager#isDeviceSecure()
          */
+        @NonNull
         public Builder setEncryptionRequired(boolean required) {
             if (required) {
                 mFlags |= KeyStore.FLAG_ENCRYPTED;
@@ -368,6 +380,7 @@
          *
          * @see #setKeyValidityEnd(Date)
          */
+        @NonNull
         public Builder setKeyValidityStart(Date startDate) {
             mKeyValidityStart = startDate;
             return this;
@@ -384,6 +397,7 @@
          * @see #setKeyValidityForConsumptionEnd(Date)
          * @see #setKeyValidityForOriginationEnd(Date)
          */
+        @NonNull
         public Builder setKeyValidityEnd(Date endDate) {
             setKeyValidityForOriginationEnd(endDate);
             setKeyValidityForConsumptionEnd(endDate);
@@ -399,6 +413,7 @@
          *
          * @see #setKeyValidityForConsumptionEnd(Date)
          */
+        @NonNull
         public Builder setKeyValidityForOriginationEnd(Date endDate) {
             mKeyValidityForOriginationEnd = endDate;
             return this;
@@ -414,6 +429,7 @@
          *
          * @see #setKeyValidityForOriginationEnd(Date)
          */
+        @NonNull
         public Builder setKeyValidityForConsumptionEnd(Date endDate) {
             mKeyValidityForConsumptionEnd = endDate;
             return this;
@@ -426,6 +442,7 @@
          *
          * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
          */
+        @NonNull
         public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) {
             mPurposes = purposes;
             return this;
@@ -440,6 +457,7 @@
          *
          * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
          */
+        @NonNull
         public Builder setEncryptionPaddings(
                 @KeyStoreKeyProperties.EncryptionPaddingEnum String... paddings) {
             mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
@@ -455,6 +473,7 @@
          *
          * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
          */
+        @NonNull
         public Builder setSignaturePaddings(
                 @KeyStoreKeyProperties.SignaturePaddingEnum String... paddings) {
             mSignaturePaddings = ArrayUtils.cloneIfNotEmpty(paddings);
@@ -471,6 +490,7 @@
          *
          * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
          */
+        @NonNull
         public Builder setDigests(@KeyStoreKeyProperties.DigestEnum String... digests) {
             mDigests = ArrayUtils.cloneIfNotEmpty(digests);
             return this;
@@ -484,6 +504,7 @@
          *
          * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
          */
+        @NonNull
         public Builder setBlockModes(@KeyStoreKeyProperties.BlockModeEnum String... blockModes) {
             mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
             return this;
@@ -525,6 +546,7 @@
          *
          * <p><b>NOTE: This has currently no effect on asymmetric key pairs.
          */
+        @NonNull
         public Builder setRandomizedEncryptionRequired(boolean required) {
             mRandomizedEncryptionRequired = required;
             return this;
@@ -547,6 +569,7 @@
          *
          * @see #setUserAuthenticationValidityDurationSeconds(int)
          */
+        @NonNull
         public Builder setUserAuthenticationRequired(boolean required) {
             mUserAuthenticationRequired = required;
             return this;
@@ -565,7 +588,9 @@
          *
          * @see #setUserAuthenticationRequired(boolean)
          */
-        public Builder setUserAuthenticationValidityDurationSeconds(int seconds) {
+        @NonNull
+        public Builder setUserAuthenticationValidityDurationSeconds(
+                @IntRange(from = -1) int seconds) {
             mUserAuthenticationValidityDurationSeconds = seconds;
             return this;
         }
@@ -576,6 +601,7 @@
          * @throws IllegalArgumentException if a required field is missing
          * @return built instance of {@code KeyStoreParameter}
          */
+        @NonNull
         public KeyStoreParameter build() {
             return new KeyStoreParameter(
                     mContext,
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index f261079..d138c24 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -817,6 +817,9 @@
 
         KeymasterArguments out = new KeymasterArguments();
         args = new KeymasterArguments();
+        args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+        args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_GCM);
+        args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
         OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT,
                 true, args, null, out);
         IBinder token = result.token;
@@ -881,14 +884,18 @@
             hexToBytes("591ccb10d410ed26dc5ba74a31362870"),
             hexToBytes("b6ed21b99ca6f4f9f153e7b1beafed1d"),
             hexToBytes("23304b7a39f9f3ff067d8d8f9e24ecc7")};
+        KeymasterArguments beginArgs = new KeymasterArguments();
+        beginArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+        beginArgs.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
+        beginArgs.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
         for (int i = 0; i < testVectors.length; i++) {
             byte[] cipherText = doOperation(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, testVectors[i],
-                    new KeymasterArguments());
+                    beginArgs);
             MoreAsserts.assertEquals(cipherVectors[i], cipherText);
         }
         for (int i = 0; i < testVectors.length; i++) {
             byte[] plainText = doOperation(name, KeymasterDefs.KM_PURPOSE_DECRYPT,
-                    cipherVectors[i], new KeymasterArguments());
+                    cipherVectors[i], beginArgs);
             MoreAsserts.assertEquals(testVectors[i], plainText);
         }
     }
@@ -912,6 +919,9 @@
 
         KeymasterArguments out = new KeymasterArguments();
         args = new KeymasterArguments();
+        args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+        args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR);
+        args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
         OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT,
                 true, args, null, out);
         assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
index af3d9b3..93b1d56 100644
--- a/libs/androidfw/ZipFileRO.cpp
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -68,6 +68,7 @@
     const int32_t error = OpenArchive(zipFileName, &handle);
     if (error) {
         ALOGW("Error opening archive %s: %s", zipFileName, ErrorCodeString(error));
+        CloseArchive(handle);
         return NULL;
     }
 
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 74964f6..8f5fea3 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -142,7 +142,7 @@
     char property[PROPERTY_VALUE_MAX];
     if (property_get(PROPERTY_PATH_CACHE_SIZE, property, nullptr) > 0) {
         INIT_LOGD("  Setting %s cache size to %sMB", name, property);
-        setMaxSize(MB(atof(property)));
+        mMaxSize = MB(atof(property));
     } else {
         INIT_LOGD("  Using default %s cache size of %.2fMB", name, DEFAULT_PATH_CACHE_SIZE);
     }
@@ -172,13 +172,6 @@
     return mMaxSize;
 }
 
-void PathCache::setMaxSize(uint32_t maxSize) {
-    mMaxSize = maxSize;
-    while (mSize > mMaxSize) {
-        mCache.removeOldest();
-    }
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 // Callbacks
 ///////////////////////////////////////////////////////////////////////////////
@@ -267,24 +260,18 @@
         PathTexture* texture, bool addToCache) {
     generateTexture(*bitmap, texture);
 
+    // Note here that we upload to a texture even if it's bigger than mMaxSize.
+    // Such an entry in mCache will only be temporary, since it will be evicted
+    // immediately on trim, or on any other Path entering the cache.
     uint32_t size = texture->width * texture->height;
-    if (size < mMaxSize) {
-        mSize += size;
-        PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d",
-                texture->id, size, mSize);
-        if (mDebugEnabled) {
-            ALOGD("Shape created, size = %d", size);
-        }
-        if (addToCache) {
-            mCache.put(entry, texture);
-        }
-    } else {
-        // It's okay to add a texture that's bigger than the cache since
-        // we'll trim the cache later when addToCache is set to false
-        if (!addToCache) {
-            mSize += size;
-        }
-        texture->cleanup = true;
+    mSize += size;
+    PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d",
+            texture->id, size, mSize);
+    if (mDebugEnabled) {
+        ALOGD("Shape created, size = %d", size);
+    }
+    if (addToCache) {
+        mCache.put(entry, texture);
     }
 }
 
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 4297693..7014863 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -186,10 +186,6 @@
     void clear();
 
     /**
-     * Sets the maximum size of the cache in bytes.
-     */
-    void setMaxSize(uint32_t maxSize);
-    /**
      * Returns the maximum size of the cache in bytes.
      */
     uint32_t getMaxSize();
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 216d9b9..c0bc6d6 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -416,7 +416,8 @@
      * <br>If the audio format is not specified or is incomplete, its sample rate will be the
      * default output sample rate of the device (see
      * {@link AudioManager#PROPERTY_OUTPUT_SAMPLE_RATE}), its channel configuration will be
-     * {@link AudioFormat#CHANNEL_IN_DEFAULT}.
+     * {@link AudioFormat#CHANNEL_IN_MONO}, and the encoding will be
+     * {@link AudioFormat#ENCODING_PCM_16BIT}.
      * <br>If the buffer size is not specified with {@link #setBufferSizeInBytes(int)},
      * the minimum buffer size for the source is used.
      */
@@ -533,7 +534,22 @@
          */
         public AudioRecord build() throws UnsupportedOperationException {
             if (mFormat == null) {
-                mFormat = new AudioFormat.Builder().build();
+                mFormat = new AudioFormat.Builder()
+                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                        .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                        .build();
+            } else {
+                if (mFormat.getEncoding() == AudioFormat.ENCODING_INVALID) {
+                    mFormat = new AudioFormat.Builder(mFormat)
+                            .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                            .build();
+                }
+                if (mFormat.getChannelMask() == AudioFormat.CHANNEL_INVALID
+                        && mFormat.getChannelIndexMask() == AudioFormat.CHANNEL_INVALID) {
+                    mFormat = new AudioFormat.Builder(mFormat)
+                            .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                            .build();
+                }
             }
             if (mAttributes == null) {
                 mAttributes = new AudioAttributes.Builder()
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index b6491d8..078fb2f 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -72,7 +72,7 @@
     void relayoutOverlayView(in IBinder sessionToken, in Rect frame, int userId);
     void removeOverlayView(in IBinder sessionToken, int userId);
 
-    void requestUnblockContent(in IBinder sessionToken, in String unblockedRating, int userId);
+    void unblockContent(in IBinder sessionToken, in String unblockedRating, int userId);
 
     void timeShiftPause(in IBinder sessionToken, int userId);
     void timeShiftResume(in IBinder sessionToken, int userId);
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index a054200..17f3984 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -45,7 +45,7 @@
     void relayoutOverlayView(in Rect frame);
     void removeOverlayView();
 
-    void requestUnblockContent(in String unblockedRating);
+    void unblockContent(in String unblockedRating);
 
     void timeShiftPause();
     void timeShiftResume();
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 95aaa7f..0191652 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -57,7 +57,7 @@
     private static final int DO_CREATE_OVERLAY_VIEW = 10;
     private static final int DO_RELAYOUT_OVERLAY_VIEW = 11;
     private static final int DO_REMOVE_OVERLAY_VIEW = 12;
-    private static final int DO_REQUEST_UNBLOCK_CONTENT = 13;
+    private static final int DO_UNBLOCK_CONTENT = 13;
     private static final int DO_TIME_SHIFT_PAUSE = 14;
     private static final int DO_TIME_SHIFT_RESUME = 15;
     private static final int DO_TIME_SHIFT_SEEK_TO = 16;
@@ -155,7 +155,7 @@
                 mTvInputSessionImpl.removeOverlayView(true);
                 break;
             }
-            case DO_REQUEST_UNBLOCK_CONTENT: {
+            case DO_UNBLOCK_CONTENT: {
                 mTvInputSessionImpl.unblockContent((String) msg.obj);
                 break;
             }
@@ -267,9 +267,9 @@
     }
 
     @Override
-    public void requestUnblockContent(String unblockedRating) {
+    public void unblockContent(String unblockedRating) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(
-                DO_REQUEST_UNBLOCK_CONTENT, unblockedRating));
+                DO_UNBLOCK_CONTENT, unblockedRating));
     }
 
     @Override
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index dca0631..705aa3d6 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1367,12 +1367,12 @@
         }
 
         /**
-         * Notifies of any structural changes (format or size) of the {@link Surface}
-         * passed by {@link #setSurface}.
+         * Notifies of any structural changes (format or size) of the surface passed in
+         * {@link #setSurface}.
          *
-         * @param format The new PixelFormat of the {@link Surface}.
-         * @param width The new width of the {@link Surface}.
-         * @param height The new height of the {@link Surface}.
+         * @param format The new PixelFormat of the surface.
+         * @param width The new width of the surface.
+         * @param height The new height of the surface.
          * @hide
          */
         @SystemApi
@@ -1820,14 +1820,14 @@
         /**
          * Requests to unblock content blocked by parental controls.
          */
-        void requestUnblockContent(@NonNull TvContentRating unblockedRating) {
+        void unblockContent(@NonNull TvContentRating unblockedRating) {
             Preconditions.checkNotNull(unblockedRating);
             if (mToken == null) {
                 Log.w(TAG, "The session has been already released");
                 return;
             }
             try {
-                mService.requestUnblockContent(mToken, unblockedRating.flattenToString(), mUserId);
+                mService.unblockContent(mToken, unblockedRating.flattenToString(), mUserId);
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index c1035b0..8b0472a 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -281,8 +281,15 @@
         }
 
         /**
-         * Enables or disables the overlay view. By default, the overlay view is disabled. Must be
-         * called explicitly after the session is created to enable the overlay view.
+         * Enables or disables the overlay view.
+         *
+         * <p>By default, the overlay view is disabled. Must be called explicitly after the
+         * session is created to enable the overlay view.
+         *
+         * <p>The TV input service can disable its overlay view when the size of the overlay view is
+         * insufficient to display the whole information, such as when used in Picture-in-picture.
+         * Override {@link #onOverlayViewSizeChanged} to get the size of the overlay view, which
+         * then can be used to determine whether to enable/disable the overlay view.
          *
          * @param enable {@code true} if you want to enable the overlay view. {@code false}
          *            otherwise.
@@ -637,17 +644,15 @@
         }
 
         /**
-         * Assigns a position of the {@link Surface} passed by {@link #onSetSurface}. The position
-         * is relative to an overlay view.
+         * Assigns a size and position to the surface passed in {@link #onSetSurface}. The position
+         * is relative to the overlay view that sits on top of this surface.
          *
          * @param left Left position in pixels, relative to the overlay view.
          * @param top Top position in pixels, relative to the overlay view.
          * @param right Right position in pixels, relative to the overlay view.
          * @param bottom Bottom position in pixels, relative to the overlay view.
          * @see #onOverlayViewSizeChanged
-         * @hide
          */
-        @SystemApi
         public void layoutSurface(final int left, final int top, final int right,
                 final int bottom) {
             if (left > right || top > bottom) {
@@ -701,41 +706,40 @@
         }
 
         /**
-         * Sets the {@link Surface} for the current input session on which the TV input renders
-         * video.
+         * Called when the application sets the surface.
          *
-         * <p>When {@code setSurface(null)} is called, the implementation should stop using the
-         * Surface object previously given and release any references to it.
+         * <p>The TV input service should render video onto the given surface. When called with
+         * {@code null}, the input service should immediately release any references to the
+         * currently set surface and stop using it.
          *
-         * @param surface possibly {@code null} {@link Surface} the application passes to this TV
-         *            input session.
-         * @return {@code true} if the surface was set, {@code false} otherwise.
+         * @param surface The surface to be used for video rendering. Can be {@code null}.
+         * @return {@code true} if the surface was set successfully, {@code false} otherwise.
          */
         public abstract boolean onSetSurface(@Nullable Surface surface);
 
         /**
-         * Called after any structural changes (format or size) have been made to the
-         * {@link Surface} passed by {@link #onSetSurface}. This method is always called
-         * at least once, after {@link #onSetSurface} with non-null {@link Surface} is called.
+         * Called after any structural changes (format or size) have been made to the surface passed
+         * in {@link #onSetSurface}. This method is always called at least once, after
+         * {@link #onSetSurface} is called with non-null surface.
          *
-         * @param format The new PixelFormat of the {@link Surface}.
-         * @param width The new width of the {@link Surface}.
-         * @param height The new height of the {@link Surface}.
+         * @param format The new PixelFormat of the surface.
+         * @param width The new width of the surface.
+         * @param height The new height of the surface.
          */
         public void onSurfaceChanged(int format, int width, int height) {
         }
 
         /**
-         * Called when a size of an overlay view is changed by the application. Even when the
-         * overlay view is disabled by {@link #setOverlayViewEnabled}, this is called. The size is
-         * same as the size of {@link Surface} in general. Once {@link #layoutSurface} is called,
-         * the sizes of {@link Surface} and the overlay view can be different.
+         * Called when the size of the overlay view is changed by the application.
+         *
+         * <p>This is always called at least once when the session is created regardless of whether
+         * the overlay view is enabled or not. The overlay view size is the same as the containing
+         * {@link TvView}. Note that the size of the underlying surface can be different if the
+         * surface was changed by calling {@link #layoutSurface}.
          *
          * @param width The width of the overlay view.
          * @param height The height of the overlay view.
-         * @hide
          */
-        @SystemApi
         public void onOverlayViewSizeChanged(int width, int height) {
         }
 
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 7e64b17..ebe281f 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -350,11 +350,27 @@
      * @param unblockedRating A TvContentRating to unblock.
      * @see TvInputService.Session#notifyContentBlocked(TvContentRating)
      * @hide
+     * @deprecated Use {@link #unblockContent} instead.
      */
+    @Deprecated
     @SystemApi
     public void requestUnblockContent(TvContentRating unblockedRating) {
+        unblockContent(unblockedRating);
+    }
+
+    /**
+     * Requests to unblock TV content according to the given rating.
+     *
+     * <p>This notifies TV input that blocked content is now OK to play.
+     *
+     * @param unblockedRating A TvContentRating to unblock.
+     * @see TvInputService.Session#notifyContentBlocked(TvContentRating)
+     * @hide
+     */
+    @SystemApi
+    public void unblockContent(TvContentRating unblockedRating) {
         if (mSession != null) {
-            mSession.requestUnblockContent(unblockedRating);
+            mSession.unblockContent(unblockedRating);
         }
     }
 
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 16c6075..3f0d71c 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -207,4 +207,7 @@
     <!-- Default for Settings.Global.GUEST_USER_ENABLED -->
     <bool name="def_guest_user_enabled">true</bool>
 
+    <!-- Default state of tap to wake -->
+    <bool name="def_double_tap_to_wake">true</bool>
+
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1953e75..ad710a6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -35,6 +35,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.hardware.camera2.utils.ArrayUtils;
+import android.media.AudioManager;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -52,9 +53,11 @@
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.BackgroundThread;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
@@ -1788,7 +1791,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 118;
+            private static final int SETTINGS_VERSION = 120;
 
             private final int mUserId;
 
@@ -1891,6 +1894,31 @@
 
                 int currentVersion = oldVersion;
 
+                // v119: Reset zen + ringer mode.
+                if (currentVersion == 118) {
+                    if (userId == UserHandle.USER_OWNER) {
+                        final SettingsState globalSettings = getGlobalSettingsLocked();
+                        globalSettings.updateSettingLocked(Settings.Global.ZEN_MODE,
+                                Integer.toString(Settings.Global.ZEN_MODE_OFF),
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                        globalSettings.updateSettingLocked(Settings.Global.MODE_RINGER,
+                                Integer.toString(AudioManager.RINGER_MODE_NORMAL),
+                                SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+                    currentVersion = 119;
+                }
+
+                // v120: Add double tap to wake setting.
+                if (currentVersion == 119) {
+                    SettingsState secureSettings = getSecureSettingsLocked(userId);
+                    secureSettings.insertSettingLocked(Settings.Secure.DOUBLE_TAP_TO_WAKE,
+                            getContext().getResources().getBoolean(
+                                    R.bool.def_double_tap_to_wake) ? "1" : "0",
+                            SettingsState.SYSTEM_PACKAGE_NAME);
+
+                    currentVersion = 120;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 // Return the current version.
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 24f6931..209a233 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -182,6 +182,20 @@
                 android:excludeFromRecents="true">
         </activity>
 
+        <activity android:name=".tuner.TunerActivity"
+                  android:enabled="false"
+                  android:icon="@drawable/icon"
+                  android:theme="@android:style/Theme.Material.Settings"
+                  android:label="@string/system_ui_tuner"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.settings.action.EXTRA_SETTINGS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data android:name="com.android.settings.category"
+                    android:value="com.android.settings.category.device" />
+        </activity>
+
         <!-- Alternate Recents -->
         <activity android:name=".recents.RecentsActivity"
                   android:label="@string/accessibility_desc_recent_apps"
diff --git a/packages/SystemUI/res/drawable/ic_dnd_total_silence.xml b/packages/SystemUI/res/drawable/ic_dnd_total_silence.xml
new file mode 100644
index 0000000..4875974
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_dnd_total_silence.xml
@@ -0,0 +1,29 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="24dp" >
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0s10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,20.5c-4.7,0.0 -8.5,-3.8 -8.5,-8.5S7.3,3.5 12.0,3.5s8.5,3.8 8.5,8.5S16.7,20.5 12.0,20.5z"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12.0,6.0c-3.3,0.0 -6.0,2.7 -6.0,6.0c0.0,3.3 2.7,6.0 6.0,6.0s6.0,-2.7 6.0,-6.0C18.0,8.7 15.4,6.0 12.0,6.0zM15.0,13.0L9.0,13.0l0.0,-2.0l6.0,0.0L15.0,13.0z"/>
+
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml b/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml
index 7617ec4..f4c20a9 100644
--- a/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_dnd_on.xml
@@ -1,7 +1,7 @@
 <!--
-Copyright (C) 2015 The Android Open Source Project
+    Copyright (C) 2015 The Android Open Source Project
 
-   Licensed under the Apache License, Version 2.0 (the "License");
+    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
 
diff --git a/packages/SystemUI/res/drawable/ic_qs_dnd_on_total_silence.xml b/packages/SystemUI/res/drawable/ic_qs_dnd_on_total_silence.xml
new file mode 100644
index 0000000..fb26c09
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_qs_dnd_on_total_silence.xml
@@ -0,0 +1,29 @@
+<!--
+    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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="64dp"
+        android:height="64dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0s10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,20.5c-4.7,0.0 -8.5,-3.8 -8.5,-8.5S7.3,3.5 12.0,3.5s8.5,3.8 8.5,8.5S16.7,20.5 12.0,20.5z"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12.0,6.0c-3.3,0.0 -6.0,2.7 -6.0,6.0c0.0,3.3 2.7,6.0 6.0,6.0s6.0,-2.7 6.0,-6.0C18.0,8.7 15.4,6.0 12.0,6.0zM15.0,13.0L9.0,13.0l0.0,-2.0l6.0,0.0L15.0,13.0z"/>
+
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_dnd.xml b/packages/SystemUI/res/drawable/stat_sys_dnd.xml
index 9361bc0..3bf5e98 100644
--- a/packages/SystemUI/res/drawable/stat_sys_dnd.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_dnd.xml
@@ -1,7 +1,7 @@
 <!--
-Copyright (C) 2015 The Android Open Source Project
+    Copyright (C) 2015 The Android Open Source Project
 
-   Licensed under the Apache License, Version 2.0 (the "License");
+    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
 
diff --git a/packages/SystemUI/res/drawable/stat_sys_dnd_total_silence.xml b/packages/SystemUI/res/drawable/stat_sys_dnd_total_silence.xml
new file mode 100644
index 0000000..82e25ca
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_dnd_total_silence.xml
@@ -0,0 +1,31 @@
+<!--
+    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.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+    android:insetLeft="2.5dp"
+    android:insetRight="2.5dp">
+    <vector
+            android:width="17dp"
+            android:height="17dp"
+            android:viewportWidth="24.0"
+            android:viewportHeight="24.0">
+        <path
+            android:fillColor="#FFFFFFFF"
+            android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0s10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,20.5c-4.7,0.0 -8.5,-3.8 -8.5,-8.5S7.3,3.5 12.0,3.5s8.5,3.8 8.5,8.5S16.7,20.5 12.0,20.5z"/>
+        <path
+            android:fillColor="#FFFFFFFF"
+            android:pathData="M12.0,6.0c-3.3,0.0 -6.0,2.7 -6.0,6.0c0.0,3.3 2.7,6.0 6.0,6.0s6.0,-2.7 6.0,-6.0C18.0,8.7 15.4,6.0 12.0,6.0zM15.0,13.0L9.0,13.0l0.0,-2.0l6.0,0.0L15.0,13.0z"/>
+    </vector>
+</inset>
diff --git a/packages/SystemUI/res/layout/volume_zen_footer.xml b/packages/SystemUI/res/layout/volume_zen_footer.xml
index b780f7d..998741c 100644
--- a/packages/SystemUI/res/layout/volume_zen_footer.xml
+++ b/packages/SystemUI/res/layout/volume_zen_footer.xml
@@ -41,8 +41,7 @@
             android:layout_width="@dimen/volume_button_size"
             android:layout_height="@dimen/volume_button_size"
             android:layout_marginEnd="7dp"
-            android:scaleType="center"
-            android:src="@drawable/ic_dnd" />
+            android:scaleType="center" />
 
         <LinearLayout
             android:layout_width="0dp"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8606a59..c74633c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -213,7 +213,7 @@
     <string name="unlock_label">unlock</string>
     <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
     <string name="phone_label">open phone</string>
-    <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
+    <!-- Click action label for accessibility for the voice assist button. This is not shown on-screen and is an accessibility label for the icon which launches the voice assist from the lock screen.[CHAR LIMIT=NONE] -->
     <string name="voice_assist_label">open voice assist</string>
     <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
     <string name="camera_label">open camera</string>
@@ -1021,4 +1021,7 @@
     <string name="volume_stream_muted_dnd" translatable="false">%s silent — Total silence</string>
     <string name="volume_stream_limited_dnd" translatable="false">%s — Priority only</string>
     <string name="volume_stream_vibrate_dnd" translatable="false">%s vibrate — Priority only</string>
+
+    <!-- Name of special SystemUI debug settings -->
+    <string name="system_ui_tuner">SystemUI Tuner</string>
 </resources>
diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml
new file mode 100644
index 0000000..263260e
--- /dev/null
+++ b/packages/SystemUI/res/xml/tuner_prefs.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        android:title="@string/system_ui_tuner">
+
+        <!-- Tuner prefs go here -->
+
+</PreferenceScreen>
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index d1f8963..08659e9 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -204,7 +204,8 @@
 
     private boolean getVoiceInteractorSupportsAssistGesture() {
         try {
-            return mVoiceInteractionManagerService.activeServiceSupportsAssist();
+            return mVoiceInteractionManagerService != null
+                    && mVoiceInteractionManagerService.activeServiceSupportsAssist();
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to call activeServiceSupportsAssistGesture", e);
             return false;
@@ -213,7 +214,8 @@
 
     public boolean canVoiceAssistBeLaunchedFromKeyguard() {
         try {
-            return mVoiceInteractionManagerService.activeServiceSupportsLaunchFromKeyguard();
+            return mVoiceInteractionManagerService != null
+                    && mVoiceInteractionManagerService.activeServiceSupportsLaunchFromKeyguard();
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to call activeServiceSupportsLaunchFromKeyguard", e);
             return false;
@@ -231,7 +233,8 @@
 
     private boolean isVoiceSessionRunning() {
         try {
-            return mVoiceInteractionManagerService.isSessionRunning();
+            return mVoiceInteractionManagerService != null
+                    && mVoiceInteractionManagerService.isSessionRunning();
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to call isSessionRunning", e);
             return false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 8205798..5f24619 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -112,7 +112,7 @@
                         R.string.accessibility_quick_settings_dnd_priority_on);
                 break;
             case Global.ZEN_MODE_NO_INTERRUPTIONS:
-                state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on);
+                state.icon = ResourceIcon.get(R.drawable.ic_qs_dnd_on_total_silence);
                 state.label = mContext.getString(R.string.quick_settings_dnd_none_label);
                 state.contentDescription = mContext.getString(
                         R.string.accessibility_quick_settings_dnd_none_on);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index fe7bc97..3eb6b13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -75,7 +75,7 @@
                 mInitialTouchY = y;
                 mInitialTouchX = x;
                 setTrackingHeadsUp(false);
-                ExpandableView child = mStackScroller.getChildAtPosition(x, y);
+                ExpandableView child = mStackScroller.getChildAtRawPosition(x, y);
                 mTouchingHeadsUpView = false;
                 if (child instanceof ExpandableNotificationRow) {
                     mPickedChild = (ExpandableNotificationRow) child;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index da65a01..8ccd222 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -619,7 +619,7 @@
                     R.color.notification_panel_solid_background)));
         }
 
-        mHeadsUpManager = new HeadsUpManager(context, mNotificationPanel.getViewTreeObserver());
+        mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow);
         mHeadsUpManager.setBar(this);
         mHeadsUpManager.addListener(this);
         mHeadsUpManager.addListener(mNotificationPanel);
@@ -1869,21 +1869,34 @@
     @Override
     public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
         if (inPinnedMode) {
+            // We need to ensure that the touchable region is updated before the window will be
+            // resized, in order to not catch any touches. A layout will ensure that
+            // onComputeInternalInsets will be called and after that we can resize the layout. Let's
+            // make sure that the window stays small for one frame until the touchableRegion is set.
+            mNotificationPanel.requestLayout();
             mStatusBarWindowManager.setHeadsUpShowing(true);
             mStatusBarWindowManager.setForceStatusBarVisible(true);
-        } else {
-            Runnable endRunnable = new Runnable() {
+            mStatusBarWindowManager.setForceWindowCollapsed(true);
+            mNotificationPanel.post(new Runnable() {
                 @Override
                 public void run() {
-                    if (!mHeadsUpManager.hasPinnedHeadsUp()) {
-                        mStatusBarWindowManager.setHeadsUpShowing(false);
-                    }
+                    mStatusBarWindowManager.setForceWindowCollapsed(false);
                 }
-            };
+            });
+        } else {
             if (!mNotificationPanel.isFullyCollapsed()) {
-                endRunnable.run();
+                mStatusBarWindowManager.setHeadsUpShowing(false);
             } else {
-                mStackScroller.runAfterAnimationFinished(endRunnable);
+                mHeadsUpManager.setHeadsUpGoingAway(true);
+                mStackScroller.runAfterAnimationFinished(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (!mHeadsUpManager.hasPinnedHeadsUp()) {
+                            mStatusBarWindowManager.setHeadsUpShowing(false);
+                            mHeadsUpManager.setHeadsUpGoingAway(false);
+                        }
+                    }
+                });
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 0e8e844..5942b46 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -219,7 +219,8 @@
 
         if (DndTile.isVisible(mContext) || DndTile.isCombinedIcon(mContext)) {
             zenVisible = mZen != Global.ZEN_MODE_OFF;
-            zenIconId = R.drawable.stat_sys_dnd;
+            zenIconId = mZen == Global.ZEN_MODE_NO_INTERRUPTIONS
+                    ? R.drawable.stat_sys_dnd_total_silence : R.drawable.stat_sys_dnd;
             zenDescription = mContext.getString(R.string.quick_settings_dnd_label);
         } else if (mZen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
             zenVisible = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index e6edbeac..acf2f57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -378,8 +378,9 @@
         if (previousAnimator != null) {
             if (animate || alpha == mCurrentHeadsUpAlpha) {
                 previousAnimator.cancel();
+            } else {
+                animEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, TAG_HUN_END_ALPHA);
             }
-            animEndValue = StackStateAnimator.getChildTag(mHeadsUpScrim, TAG_HUN_START_ALPHA);
         }
         if (alpha != mCurrentHeadsUpAlpha && alpha != animEndValue) {
             if (animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index e7e4384..422d868 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -129,8 +129,9 @@
     }
 
     private void applyHeight(State state) {
-        boolean expanded = state.isKeyguardShowingAndNotOccluded() || state.statusBarExpanded
-                || state.keyguardFadingAway || state.bouncerShowing || state.headsUpShowing;
+        boolean expanded = !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
+                || state.statusBarExpanded || state.keyguardFadingAway || state.bouncerShowing
+                || state.headsUpShowing);
         if (expanded) {
             mLpChanged.height = ViewGroup.LayoutParams.MATCH_PARENT;
         } else {
@@ -256,6 +257,16 @@
         apply(mCurrentState);
     }
 
+    /**
+     * Force the window to be collapsed, even if it should theoretically be expanded.
+     * Used for when a heads-up comes in but we still need to wait for the touchable regions to
+     * be computed.
+     */
+    public void setForceWindowCollapsed(boolean force) {
+        mCurrentState.forceCollapsed = force;
+        apply(mCurrentState);
+    }
+
     private static class State {
         boolean keyguardShowing;
         boolean keyguardOccluded;
@@ -267,6 +278,7 @@
         boolean qsExpanded;
         boolean headsUpShowing;
         boolean forceStatusBarVisible;
+        boolean forceCollapsed;
 
         /**
          * The {@link BaseStatusBar} state from the status bar.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 0db9221..98822a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -25,6 +25,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pools;
+import android.view.View;
 import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityEvent;
 
@@ -78,6 +79,8 @@
         }
     };
 
+    private final View mStatusBarWindowView;
+    private final int mStatusBarHeight;
     private PhoneStatusBar mBar;
     private int mSnoozeLengthMs;
     private ContentObserver mSettingsObserver;
@@ -92,8 +95,11 @@
     private boolean mIsExpanded;
     private boolean mHasPinnedNotification;
     private int[] mTmpTwoArray = new int[2];
+    private boolean mHeadsUpGoingAway;
+    private boolean mWaitingOnCollapseWhenGoingAway;
+    private boolean mIsObserving;
 
-    public HeadsUpManager(final Context context, ViewTreeObserver observer) {
+    public HeadsUpManager(final Context context, View statusBarWindowView) {
         Resources resources = context.getResources();
         mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
         mSnoozedPackages = new ArrayMap<>();
@@ -119,7 +125,24 @@
         context.getContentResolver().registerContentObserver(
                 Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
                 mSettingsObserver);
-        observer.addOnComputeInternalInsetsListener(this);
+        mStatusBarWindowView = statusBarWindowView;
+        mStatusBarHeight = resources.getDimensionPixelSize(
+                com.android.internal.R.dimen.status_bar_height);
+    }
+
+    private void updateTouchableRegionListener() {
+        boolean shouldObserve = mHasPinnedNotification || mHeadsUpGoingAway
+                || mWaitingOnCollapseWhenGoingAway;
+        if (shouldObserve == mIsObserving) {
+            return;
+        }
+        if (shouldObserve) {
+            mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+            mStatusBarWindowView.requestLayout();
+        } else {
+            mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+        }
+        mIsObserving = shouldObserve;
     }
 
     public void setBar(PhoneStatusBar bar) {
@@ -207,6 +230,7 @@
             return;
         }
         mHasPinnedNotification = hasPinnedNotification;
+        updateTouchableRegionListener();
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpPinnedModeChanged(hasPinnedNotification);
         }
@@ -326,7 +350,7 @@
     }
 
     public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
-        if (!mIsExpanded && mHasPinnedNotification) {
+        if (mHasPinnedNotification) {
             int minX = Integer.MAX_VALUE;
             int maxX = 0;
             int minY = Integer.MAX_VALUE;
@@ -344,6 +368,9 @@
 
             info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
             info.touchableRegion.set(minX, minY, maxX, maxY);
+        } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
+            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+            info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
         }
     }
 
@@ -419,6 +446,10 @@
             mIsExpanded = isExpanded;
             if (isExpanded) {
                 unpinAll();
+                // make sure our state is sane
+                mWaitingOnCollapseWhenGoingAway = false;
+                mHeadsUpGoingAway = false;
+                updateTouchableRegionListener();
             }
         }
     }
@@ -443,6 +474,40 @@
         return aEntry.compareTo(bEntry);
     }
 
+    /**
+     * Set that we are exiting the headsUp pinned mode, but some notifications might still be
+     * animating out. This is used to keep the touchable regions in a sane state.
+     */
+    public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
+        if (headsUpGoingAway != mHeadsUpGoingAway) {
+            mHeadsUpGoingAway = headsUpGoingAway;
+            if (!headsUpGoingAway) {
+                waitForStatusBarLayout();
+            }
+            updateTouchableRegionListener();
+        }
+    }
+
+    /**
+     * We need to wait on the whole panel to collapse, before we can remove the touchable region
+     * listener.
+     */
+    private void waitForStatusBarLayout() {
+        mWaitingOnCollapseWhenGoingAway = true;
+        mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                    int oldLeft,
+                    int oldTop, int oldRight, int oldBottom) {
+                if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
+                    mStatusBarWindowView.removeOnLayoutChangeListener(this);
+                    mWaitingOnCollapseWhenGoingAway = false;
+                    updateTouchableRegionListener();
+                }
+            }
+        });
+    }
+
 
     /**
      * This represents a notification and how long it is in a heads up mode. It also manages its
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
new file mode 100644
index 0000000..c84f618
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerActivity.java
@@ -0,0 +1,30 @@
+/*
+ * 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 com.android.systemui.tuner;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class TunerActivity extends Activity {
+
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        getFragmentManager().beginTransaction().replace(android.R.id.content, new TunerFragment())
+                .commit();
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
new file mode 100644
index 0000000..df1b0d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -0,0 +1,44 @@
+/*
+ * 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 com.android.systemui.tuner;
+
+import android.os.Bundle;
+import android.preference.PreferenceFragment;
+import android.view.MenuItem;
+
+import com.android.systemui.R;
+
+public class TunerFragment extends PreferenceFragment {
+
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        addPreferencesFromResource(R.xml.tuner_prefs);
+        getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
+        setHasOptionsMenu(true);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                getActivity().finish();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
index ccb2b5a..66c4993 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -21,6 +21,7 @@
 import android.service.notification.ZenModeConfig;
 import android.util.AttributeSet;
 import android.view.View;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -38,6 +39,7 @@
     private final Context mContext;
     private final SpTexts mSpTexts;
 
+    private ImageView mIcon;
     private TextView mSummaryLine1;
     private TextView mSummaryLine2;
     private TextView mEndNowButton;
@@ -55,6 +57,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mIcon = (ImageView) findViewById(R.id.volume_zen_icon);
         mSummaryLine1 = (TextView) findViewById(R.id.volume_zen_summary_line_1);
         mSummaryLine2 = (TextView) findViewById(R.id.volume_zen_summary_line_2);
         mEndNowButton = (TextView) findViewById(R.id.volume_zen_end_now);
@@ -115,6 +118,7 @@
     }
 
     public void update() {
+        mIcon.setImageResource(isZenNone() ? R.drawable.ic_dnd_total_silence : R.drawable.ic_dnd);
         final String line1 =
                 isZenPriority() ? mContext.getString(R.string.interruption_level_priority)
                 : isZenAlarms() ? mContext.getString(R.string.interruption_level_alarms)
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index 3b61f9d..70a5821 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -1422,6 +1422,8 @@
     }
 
     /**
+     * @hide
+     *
      * This is only intended to be used by auto-generated code reflected from
      * the RenderScript script files and should not be used by developers.
      *
diff --git a/rs/java/android/renderscript/ScriptIntrinsicBLAS.java b/rs/java/android/renderscript/ScriptIntrinsicBLAS.java
index 5e28d3f..f7e81b0 100644
--- a/rs/java/android/renderscript/ScriptIntrinsicBLAS.java
+++ b/rs/java/android/renderscript/ScriptIntrinsicBLAS.java
@@ -1039,14 +1039,8 @@
             if (cM != cN) {
                 throw new RSRuntimeException("Matrix C is not symmetric");
             }
-            if (TransA != NO_TRANSPOSE) {
-                if (aN != cM) {
-                    throw new RSRuntimeException("Called BLAS with invalid dimensions");
-                }
-            } else {
-                if (aM != cM) {
-                    throw new RSRuntimeException("Called BLAS with invalid dimensions");
-                }
+            if (aM != cM) {
+                throw new RSRuntimeException("Called BLAS with invalid dimensions");
             }
         } else if (A != null && B != null) {
             // A and B only
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index a49fb76..1833a1c 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -43,9 +43,6 @@
 
 //#define LOG_API ALOGE
 static constexpr bool kLogApi = false;
-static constexpr size_t kMaxNumberArgsAndBindings = 1000;
-static constexpr size_t kMaxNumberClosuresInScriptGroup = 1000000;
-static constexpr size_t kMaxNumberKernelArguments = 256;
 
 using namespace android;
 
@@ -371,7 +368,7 @@
       goto exit;
   }
 
-  if (numValues > kMaxNumberArgsAndBindings) {
+  if (numValues > RS_CLOSURE_MAX_NUMBER_ARGS_AND_BINDINGS) {
       ALOGE("Too many arguments or globals in closure creation");
       goto exit;
   }
@@ -456,7 +453,7 @@
 
   numValues = (size_t) fieldIDs_length;
 
-  if (numValues > kMaxNumberArgsAndBindings) {
+  if (numValues > RS_CLOSURE_MAX_NUMBER_ARGS_AND_BINDINGS) {
       ALOGE("Too many arguments or globals in closure creation");
       goto exit;
   }
@@ -521,7 +518,7 @@
 
   RsClosure* closures;
 
-  if (numClosures > (jsize) kMaxNumberClosuresInScriptGroup) {
+  if (numClosures > (jsize) RS_SCRIPT_GROUP_MAX_NUMBER_CLOSURES) {
     ALOGE("Too many closures in script group");
     goto exit;
   }
@@ -1867,7 +1864,7 @@
 
     if (ains != nullptr) {
         in_len = _env->GetArrayLength(ains);
-        if (in_len > (jint)kMaxNumberKernelArguments) {
+        if (in_len > (jint)RS_KERNEL_MAX_ARGUMENTS) {
             ALOGE("Too many arguments in kernel launch.");
             // TODO (b/20758983): Report back to Java and throw an exception
             return;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 32b91d2..f5d27f9 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -41,6 +41,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -52,6 +53,7 @@
 import android.util.Log;
 
 import java.io.FileDescriptor;
+import java.io.IOException;
 import java.io.PrintWriter;
 
 import java.util.HashMap;
@@ -1737,17 +1739,32 @@
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
 
-        writer.println("enabled: " + mEnable);
-        writer.println("state: " + mState);
-        writer.println("address: " + mAddress);
-        writer.println("name: " + mName);
+        writer.println("Bluetooth Status");
+        writer.println("  enabled: " + mEnable);
+        writer.println("  state: " + mState);
+        writer.println("  address: " + mAddress);
+        writer.println("  name: " + mName + "\n");
+        writer.flush();
+
         if (mBluetooth == null) {
             writer.println("Bluetooth Service not connected");
         } else {
+            ParcelFileDescriptor pfd = null;
             try {
-                writer.println(mBluetooth.dump());
+                pfd = ParcelFileDescriptor.dup(fd);
+                mBluetooth.dump(pfd);
             } catch (RemoteException re) {
                 writer.println("RemoteException while calling Bluetooth Service");
+            } catch (IOException ioe) {
+                writer.println("IOException attempting to dup() fd");
+            } finally {
+                if (pfd != null) {
+                    try {
+                        pfd.close();
+                    } catch (IOException ioe) {
+                        writer.println("IOException attempting to close() fd");
+                    }
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index b7bc0f0..0b33812 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -142,6 +142,7 @@
     private PendingIntent mAlarmIntent;
     private Intent mIdleIntent;
     private Display mCurDisplay;
+    private boolean mIdleDisabled;
     private boolean mScreenOn;
     private boolean mCharging;
     private boolean mSigMotionActive;
@@ -187,10 +188,16 @@
     private final ArrayMap<String, Integer> mPowerSaveWhitelistUserApps = new ArrayMap<>();
 
     /**
-     * UIDs that have been white-listed to opt out of power save restrictions.
+     * App IDs that have been white-listed to opt out of power save restrictions.
      */
     private final SparseBooleanArray mPowerSaveWhitelistAppIds = new SparseBooleanArray();
 
+    /**
+     * Current app IDs that are in the complete power save white list.  This array can
+     * be shared with others because it will not be modified once set.
+     */
+    private int[] mPowerSaveWhitelistAppIdArray = new int[0];
+
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override public void onReceive(Context context, Intent intent) {
             if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
@@ -306,6 +313,10 @@
             return getAppIdWhitelistInternal();
         }
 
+        @Override public boolean isPowerSaveWhitelistApp(String name) {
+            return isPowerSaveWhitelistAppInternal(name);
+        }
+
         @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             DeviceIdleController.this.dump(fd, pw, args);
         }
@@ -381,6 +392,8 @@
                 filter.addAction(ACTION_STEP_IDLE_STATE);
                 getContext().registerReceiver(mReceiver, filter);
 
+                mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAppIdArray);
+
                 mDisplayManager.registerDisplayListener(mDisplayListener, null);
                 updateDisplayLocked();
             }
@@ -443,14 +456,16 @@
         }
     }
 
+    public boolean isPowerSaveWhitelistAppInternal(String packageName) {
+        synchronized (this) {
+            return mPowerSaveWhitelistApps.containsKey(packageName)
+                    || mPowerSaveWhitelistUserApps.containsKey(packageName);
+        }
+    }
+
     public int[] getAppIdWhitelistInternal() {
         synchronized (this) {
-            int size = mPowerSaveWhitelistAppIds.size();
-            int[] appids = new int[size];
-            for (int i = 0; i < size; i++) {
-                appids[i] = mPowerSaveWhitelistAppIds.keyAt(i);
-            }
-            return appids;
+            return mPowerSaveWhitelistAppIdArray;
         }
     }
 
@@ -499,7 +514,7 @@
     }
 
     void becomeInactiveIfAppropriateLocked() {
-        if (!mScreenOn && !mCharging && mState == STATE_ACTIVE) {
+        if (!mScreenOn && !mCharging && !mIdleDisabled && mState == STATE_ACTIVE) {
             // Screen has turned off; we are now going to become inactive and start
             // waiting to see if we will ultimately go idle.
             mState = STATE_INACTIVE;
@@ -625,6 +640,15 @@
         for (int i=0; i<mPowerSaveWhitelistUserApps.size(); i++) {
             mPowerSaveWhitelistAppIds.put(mPowerSaveWhitelistUserApps.valueAt(i), true);
         }
+        int size = mPowerSaveWhitelistAppIds.size();
+        int[] appids = new int[size];
+        for (int i = 0; i < size; i++) {
+            appids[i] = mPowerSaveWhitelistAppIds.keyAt(i);
+        }
+        mPowerSaveWhitelistAppIdArray = appids;
+        if (mLocalPowerManager != null) {
+            mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAppIdArray);
+        }
     }
 
     private void reportPowerSaveWhitelistChangedLocked() {
@@ -763,6 +787,10 @@
         pw.println("Commands:");
         pw.println("  step");
         pw.println("    Immediately step to next state, without waiting for alarm.");
+        pw.println("  disable");
+        pw.println("    Completely disable device idle mode.");
+        pw.println("  enable");
+        pw.println("    Re-enable device idle mode after it had previously been disabled.");
         pw.println("  whitelist");
         pw.println("    Add (prefix with +) or remove (prefix with -) packages.");
     }
@@ -782,12 +810,32 @@
                 if ("-h".equals(arg)) {
                     dumpHelp(pw);
                     return;
+                } else if ("-a".equals(arg)) {
+                    // Ignore, we always dump all.
                 } else if ("step".equals(arg)) {
                     synchronized (this) {
                         stepIdleStateLocked();
                         pw.print("Stepped to: "); pw.println(stateToString(mState));
                     }
                     return;
+                } else if ("disable".equals(arg)) {
+                    synchronized (this) {
+                        if (!mIdleDisabled) {
+                            mIdleDisabled = true;
+                            becomeActiveLocked("disabled");
+                            pw.println("Idle mode disabled");
+                        }
+                    }
+                    return;
+                } else if ("enable".equals(arg)) {
+                    synchronized (this) {
+                        if (mIdleDisabled) {
+                            mIdleDisabled = false;
+                            becomeInactiveIfAppropriateLocked();
+                            pw.println("Idle mode enabled");
+                        }
+                    }
+                    return;
                 } else if ("whitelist".equals(arg)) {
                     i++;
                     while (i < args.length) {
@@ -853,6 +901,7 @@
             }
             pw.print("  mSigMotionSensor="); pw.println(mSigMotionSensor);
             pw.print("  mCurDisplay="); pw.println(mCurDisplay);
+            pw.print("  mIdleDisabled="); pw.println(mIdleDisabled);
             pw.print("  mScreenOn="); pw.println(mScreenOn);
             pw.print("  mCharging="); pw.println(mCharging);
             pw.print("  mSigMotionActive="); pw.println(mSigMotionActive);
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
index c79fdfc..3fdef1d 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -52,9 +52,9 @@
  * 2) ASHMEM_SIZE (for scratch space used during dumping)
  * 3) ASHMEM_SIZE * HISTORY_SIZE
  *
- * Currently ASHMEM_SIZE is 256 bytes and HISTORY_SIZE is 10. Assuming
+ * Currently ASHMEM_SIZE is 256 bytes and HISTORY_SIZE is 20. Assuming
  * the system then also has 10 active rendering processes in the worst case
- * this would end up using under 10KiB (8KiB for the buffers, plus some overhead
+ * this would end up using under 14KiB (12KiB for the buffers, plus some overhead
  * for userId, pid, package name, and a couple other objects)
  *
  *  @hide */
@@ -63,7 +63,7 @@
 
     private static final String TAG = "GraphicsStatsService";
     private static final int ASHMEM_SIZE = 256;
-    private static final int HISTORY_SIZE = 10;
+    private static final int HISTORY_SIZE = 20;
 
     private final Context mContext;
     private final Object mLock = new Object();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 10855e2..c38fc8d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16906,6 +16906,9 @@
                 if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
                     intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
                     intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                    if (!mProcessesReady) {
+                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    }
                     broadcastIntentLocked(null, null, intent,
                             null, null, 0, null, null, null, AppOpsManager.OP_NONE,
                             false, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
@@ -19490,7 +19493,7 @@
             mStackSupervisor.resumeTopActivitiesLocked();
         }
         EventLogTags.writeAmSwitchUser(newUserId);
-        getUserManagerLocked().userForeground(newUserId);
+        getUserManagerLocked().onUserForeground(newUserId);
         sendUserSwitchBroadcastsLocked(oldUserId, newUserId);
     }
 
@@ -19956,7 +19959,7 @@
         public ComponentName getHomeActivityForUser(int userId) {
             synchronized (ActivityManagerService.this) {
                 ActivityRecord homeActivity = mStackSupervisor.getHomeActivityForUser(userId);
-                return homeActivity.realActivity;
+                return homeActivity == null ? null : homeActivity.realActivity;
             }
         }
     }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index aa365ea..2149b7a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -20,6 +20,7 @@
 import static android.media.AudioManager.RINGER_MODE_NORMAL;
 import static android.media.AudioManager.RINGER_MODE_SILENT;
 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
+import static android.os.Process.FIRST_APPLICATION_UID;
 
 import android.Manifest;
 import android.app.ActivityManager;
@@ -5024,6 +5025,10 @@
         }
         for (int j = packages.size() - 1; j >= 0; j--) {
             PackageInfo pkg = packages.get(j);
+            // Skip system processes
+            if (UserHandle.getAppId(pkg.applicationInfo.uid) < FIRST_APPLICATION_UID) {
+                continue;
+            }
             if (homeActivityName != null
                     && pkg.packageName.equals(homeActivityName.getPackageName())
                     && pkg.applicationInfo.isSystemApp()) {
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index ed8519a..0faccc6 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -16,10 +16,12 @@
 
 package com.android.server.fingerprint;
 
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.os.Binder;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -38,6 +40,7 @@
 import static android.Manifest.permission.MANAGE_FINGERPRINT;
 import static android.Manifest.permission.USE_FINGERPRINT;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -116,6 +119,7 @@
     static native int nativeCloseHal();
     static native void nativeInit(MessageQueue queue, FingerprintService service);
     static native long nativeGetAuthenticatorId();
+    static native int nativeSetActiveGroup(int gid, byte[] storePath);
 
     static final class FpHalMsg {
         int type; // Type of the message. One of the constants in fingerprint.h
@@ -628,6 +632,11 @@
     public void onStart() {
         publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
         mHalDeviceId = nativeOpenHal();
+        if (mHalDeviceId != 0) {
+            int userId = ActivityManager.getCurrentUser();
+            File path = Environment.getUserSystemDirectory(userId);
+            nativeSetActiveGroup(0, path.getAbsolutePath().getBytes());
+        }
         if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
     }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 65949bf..8086461 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -765,8 +765,9 @@
                     // If we don't have a media button receiver to fall back on
                     // include non-playing sessions for dispatching
                     UserRecord ur = mUserRecords.get(ActivityManager.getCurrentUser());
-                    boolean useNotPlayingSessions = ur.mLastMediaButtonReceiver == null
-                            && ur.mRestoredMediaButtonReceiver == null;
+                    boolean useNotPlayingSessions = (ur == null) ||
+                            (ur.mLastMediaButtonReceiver == null
+                                && ur.mRestoredMediaButtonReceiver == null);
                     MediaSessionRecord session = mPriorityStack
                             .getDefaultMediaButtonSession(mCurrentUserId, useNotPlayingSessions);
                     if (isVoiceKey(keyEvent.getKeyCode())) {
diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java
index 30f8b37..18407c9 100644
--- a/services/core/java/com/android/server/pm/BasePermission.java
+++ b/services/core/java/com/android/server/pm/BasePermission.java
@@ -20,8 +20,6 @@
 import android.content.pm.PermissionInfo;
 import android.os.UserHandle;
 
-import com.android.internal.util.ArrayUtils;
-
 final class BasePermission {
     final static int TYPE_NORMAL = 0;
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a120c1f..477af72 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -206,6 +206,7 @@
 import com.android.server.SystemConfig;
 import com.android.server.Watchdog;
 import com.android.server.pm.Settings.DatabaseVersion;
+import com.android.server.pm.PermissionsState.PermissionState;
 import com.android.server.storage.DeviceStorageMonitorInternal;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -304,6 +305,8 @@
 
     static final int REMOVE_CHATTY = 1<<16;
 
+    private static final int[] EMPTY_INT_ARRAY = new int[0];
+
     /**
      * Timeout (in milliseconds) after which the watchdog should declare that
      * our handler thread is wedged.  The usual default for such things is one
@@ -3139,18 +3142,26 @@
         }
     }
 
+    private static void enforceOnlySystemUpdatesPermissionPolicyFlags(int flagMask, int flagValues) {
+        if (((flagMask & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0
+                || (flagValues & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0)
+                && getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Only the system can modify policy flags");
+        }
+    }
+
     @Override
-    public void grantPermission(String packageName, String name, int userId) {
+    public void grantRuntimePermission(String packageName, String name, int userId) {
         if (!sUserManager.exists(userId)) {
             return;
         }
 
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
-                "grantPermission");
+                "grantRuntimePermission");
 
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
-                "grantPermission");
+                "grantRuntimePermission");
 
         boolean gidsChanged = false;
         final SettingBase sb;
@@ -3197,17 +3208,17 @@
     }
 
     @Override
-    public void revokePermission(String packageName, String name, int userId) {
+    public void revokeRuntimePermission(String packageName, String name, int userId) {
         if (!sUserManager.exists(userId)) {
             return;
         }
 
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
-                "revokePermission");
+                "revokeRuntimePermission");
 
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
-                "revokePermission");
+                "revokeRuntimePermission");
 
         final SettingBase sb;
 
@@ -3236,7 +3247,7 @@
                 return;
             }
 
-            // Critical, after this call all should never have the permission.
+            // Critical, after this call app should never have the permission.
             mSettings.writeRuntimePermissionsForUserLPr(userId, true);
         }
 
@@ -3244,6 +3255,86 @@
     }
 
     @Override
+    public int getPermissionFlags(String name, String packageName, int userId) {
+        if (!sUserManager.exists(userId)) {
+            return 0;
+        }
+
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
+                "getPermissionFlags");
+
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+                "getPermissionFlags");
+
+        synchronized (mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+
+            final BasePermission bp = mSettings.mPermissions.get(name);
+            if (bp == null) {
+                throw new IllegalArgumentException("Unknown permission: " + name);
+            }
+
+            SettingBase sb = (SettingBase) pkg.mExtras;
+            if (sb == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+
+            PermissionsState permissionsState = sb.getPermissionsState();
+            return permissionsState.getPermissionFlags(name, userId);
+        }
+    }
+
+    @Override
+    public void updatePermissionFlags(String name, String packageName, int flagMask,
+            int flagValues, int userId) {
+        if (!sUserManager.exists(userId)) {
+            return;
+        }
+
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
+                "updatePermissionFlags");
+
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+                "updatePermissionFlags");
+
+        enforceOnlySystemUpdatesPermissionPolicyFlags(flagMask, flagValues);
+
+        synchronized (mPackages) {
+            final PackageParser.Package pkg = mPackages.get(packageName);
+            if (pkg == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+
+            final BasePermission bp = mSettings.mPermissions.get(name);
+            if (bp == null) {
+                throw new IllegalArgumentException("Unknown permission: " + name);
+            }
+
+            SettingBase sb = (SettingBase) pkg.mExtras;
+            if (sb == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
+            }
+
+            PermissionsState permissionsState = sb.getPermissionsState();
+
+            if (permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues)) {
+                // Install and runtime permissions are stored in different places,
+                // so figure out what permission changed and persist the change.
+                if (permissionsState.getInstallPermissionState(name) != null) {
+                    scheduleWriteSettingsLocked();
+                } else if (permissionsState.getRuntimePermissionState(name, userId) != null) {
+                    mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+                }
+            }
+        }
+    }
+
+    @Override
     public boolean isProtectedBroadcast(String actionName) {
         synchronized (mPackages) {
             return mProtectedBroadcasts.contains(actionName);
@@ -7530,8 +7621,8 @@
 
         final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
 
-        int[] upgradeUserIds = PermissionsState.USERS_NONE;
-        int[] changedRuntimePermissionUserIds = PermissionsState.USERS_NONE;
+        int[] upgradeUserIds = EMPTY_INT_ARRAY;
+        int[] changedRuntimePermissionUserIds = EMPTY_INT_ARRAY;
 
         boolean changedInstallPermission = false;
 
@@ -7657,11 +7748,18 @@
                         // Grant previously granted runtime permissions.
                         for (int userId : UserManagerService.getInstance().getUserIds()) {
                             if (origPermissions.hasRuntimePermission(bp.name, userId)) {
+                                PermissionState permissionState = origPermissions
+                                        .getRuntimePermissionState(bp.name, userId);
+                                final int flags = permissionState.getFlags();
                                 if (permissionsState.grantRuntimePermission(bp, userId) ==
                                         PermissionsState.PERMISSION_OPERATION_FAILURE) {
                                     // If we cannot put the permission as it was, we have to write.
                                     changedRuntimePermissionUserIds = ArrayUtils.appendInt(
                                             changedRuntimePermissionUserIds, userId);
+                                } else {
+                                    // Propagate the permission flags.
+                                    permissionsState.updatePermissionFlags(bp, userId,
+                                            flags, flags);
                                 }
                             }
                         }
@@ -7669,13 +7767,28 @@
 
                     case GRANT_UPGRADE: {
                         // Grant runtime permissions for a previously held install permission.
-                        permissionsState.revokeInstallPermission(bp);
-                        for (int userId : upgradeUserIds) {
-                            if (permissionsState.grantRuntimePermission(bp, userId) !=
-                                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                                // If we granted the permission, we have to write.
-                                changedRuntimePermissionUserIds = ArrayUtils.appendInt(
-                                        changedRuntimePermissionUserIds, userId);
+                        PermissionState permissionState = origPermissions
+                                .getInstallPermissionState(bp.name);
+                        final int flags = permissionState != null ? permissionState.getFlags() : 0;
+
+                        origPermissions.revokeInstallPermission(bp);
+                        // We will be transferring the permission flags, so clear them.
+                        origPermissions.updatePermissionFlags(bp, UserHandle.USER_ALL,
+                                PackageManager.MASK_PERMISSION_FLAGS, 0);
+
+                        // If the permission is not to be promoted to runtime we ignore it and
+                        // also its other flags as they are not applicable to install permissions.
+                        if ((flags & PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE) == 0) {
+                            for (int userId : upgradeUserIds) {
+                                if (permissionsState.grantRuntimePermission(bp, userId) !=
+                                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                                    // Transfer the permission flags.
+                                    permissionsState.updatePermissionFlags(bp, userId,
+                                            flags, flags);
+                                    // If we granted the permission, we have to write.
+                                    changedRuntimePermissionUserIds = ArrayUtils.appendInt(
+                                            changedRuntimePermissionUserIds, userId);
+                                }
                             }
                         }
                     } break;
@@ -7692,6 +7805,9 @@
             } else {
                 if (permissionsState.revokeInstallPermission(bp) !=
                         PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                    // Also drop the permission flags.
+                    permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
+                            PackageManager.MASK_PERMISSION_FLAGS, 0);
                     changedInstallPermission = true;
                     Slog.i(TAG, "Un-granting permission " + perm
                             + " from package " + pkg.packageName
@@ -9101,7 +9217,9 @@
         synchronized (mPackages) {
             result = mSettings.updateIntentFilterVerificationStatusLPw(packageName, status, userId);
         }
-        scheduleWritePackageRestrictionsLocked(userId);
+        if (result) {
+            scheduleWritePackageRestrictionsLocked(userId);
+        }
         return result;
     }
 
@@ -9138,9 +9256,11 @@
     public boolean setDefaultBrowserPackageName(String packageName, int userId) {
         synchronized (mPackages) {
             boolean result = mSettings.setDefaultBrowserPackageNameLPr(packageName, userId);
-            result |= updateIntentVerificationStatus(packageName,
-                    PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
-                    UserHandle.myUserId());
+            if (packageName != null) {
+                result |= updateIntentVerificationStatus(packageName,
+                        PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
+                        UserHandle.myUserId());
+            }
             return result;
         }
     }
@@ -11854,6 +11974,7 @@
             if (deletedPs != null) {
                 if ((flags&PackageManager.DELETE_KEEP_DATA) == 0) {
                     clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
+                    clearDefaultBrowserIfNeeded(packageName);
                     if (outInfo != null) {
                         mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
                         outInfo.removedAppId = mSettings.removePackageLPw(packageName);
@@ -12388,7 +12509,7 @@
                     succeded = deleteApplicationCacheFilesLI(packageName, userId);
                 }
                 clearExternalStorageDataSync(packageName, userId, false);
-                if(observer != null) {
+                if (observer != null) {
                     try {
                         observer.onRemoveCompleted(packageName, succeded);
                     } catch (RemoteException e) {
@@ -12757,6 +12878,17 @@
         }
     }
 
+
+    void clearDefaultBrowserIfNeeded(String packageName) {
+        for (int oneUserId : sUserManager.getUserIds()) {
+            String defaultBrowserPackageName = getDefaultBrowserPackageName(oneUserId);
+            if (TextUtils.isEmpty(defaultBrowserPackageName)) continue;
+            if (packageName.equals(defaultBrowserPackageName)) {
+                setDefaultBrowserPackageName(null, oneUserId);
+            }
+        }
+    }
+
     @Override
     public void resetPreferredActivities(int userId) {
         /* TODO: Actually use userId. Why is it being passed in? */
diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java
index 3749957..171a50d 100644
--- a/services/core/java/com/android/server/pm/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/PermissionsState.java
@@ -20,10 +20,13 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
+import android.util.SparseArray;
 import com.android.internal.util.ArrayUtils;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -55,15 +58,8 @@
     /** The permission operation failed. */
     public static final int PERMISSION_OPERATION_FAILURE = 3;
 
-    public static final int[] USERS_ALL = {UserHandle.USER_ALL};
-
-    public static final int[] USERS_NONE = {};
-
     private static final int[] NO_GIDS = {};
 
-    private static final int FLAG_INSTALL_PERMISSIONS = 1 << 0;
-    private static final int FLAG_RUNTIME_PERMISSIONS = 1 << 1;
-
     private ArrayMap<String, PermissionData> mPermissions;
 
     private int[] mGlobalGids = NO_GIDS;
@@ -147,14 +143,16 @@
     }
 
     /**
-     * Grant a runtime permission.
+     * Grant a runtime permission for a given device user.
      *
      * @param permission The permission to grant.
+     * @param userId The device user id.
      * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
      *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
      *     #PERMISSION_OPERATION_FAILURE}.
      */
     public int grantRuntimePermission(BasePermission permission, int userId) {
+        enforceValidUserId(userId);
         if (userId == UserHandle.USER_ALL) {
             return PERMISSION_OPERATION_FAILURE;
         }
@@ -162,15 +160,18 @@
     }
 
     /**
-     * Revoke a runtime permission for a given device user.
+     *  Revoke a runtime permission for a given device user.
      *
      * @param permission The permission to revoke.
      * @param userId The device user id.
      * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
      *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
      *     #PERMISSION_OPERATION_FAILURE}.
+     *
+     * @see android.content.pm.PackageManager.PermissionFlags
      */
     public int revokeRuntimePermission(BasePermission permission, int userId) {
+        enforceValidUserId(userId);
         if (userId == UserHandle.USER_ALL) {
             return PERMISSION_OPERATION_FAILURE;
         }
@@ -178,17 +179,6 @@
     }
 
     /**
-     * Gets whether this state has a given permission, regardless if
-     * it is install time or runtime one.
-     *
-     * @param name The permission name.
-     * @return Whether this state has the permission.
-     */
-    public boolean hasPermission(String name) {
-        return mPermissions != null && mPermissions.get(name) != null;
-    }
-
-    /**
      * Gets whether this state has a given runtime permission for a
      * given device user id.
      *
@@ -197,6 +187,7 @@
      * @return Whether this state has the permission.
      */
     public boolean hasRuntimePermission(String name, int userId) {
+        enforceValidUserId(userId);
         return !hasInstallPermission(name) && hasPermission(name, userId);
     }
 
@@ -211,36 +202,6 @@
     }
 
     /**
-     * Revokes a permission for all users regardless if it is an install or
-     * a runtime permission.
-     *
-     * @param permission The permission to revoke.
-     * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
-     *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
-     *     #PERMISSION_OPERATION_FAILURE}.
-     */
-    public int revokePermission(BasePermission permission) {
-        if (!hasPermission(permission.name)) {
-            return PERMISSION_OPERATION_FAILURE;
-        }
-
-        int result = PERMISSION_OPERATION_SUCCESS;
-
-        PermissionData permissionData = mPermissions.get(permission.name);
-        for (int userId : permissionData.getUserIds()) {
-            if (revokePermission(permission, userId)
-                    == PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
-                result = PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
-                break;
-            }
-        }
-
-        mPermissions.remove(permission.name);
-
-        return result;
-    }
-
-    /**
      * Gets whether the state has a given permission for the specified
      * user, regardless if this is an install or a runtime permission.
      *
@@ -256,20 +217,7 @@
         }
 
         PermissionData permissionData = mPermissions.get(name);
-        return permissionData != null && permissionData.hasUserId(userId);
-    }
-
-    /**
-     * Gets all permissions regardless if they are install or runtime.
-     *
-     * @return The permissions or an empty set.
-     */
-    public Set<String> getPermissions() {
-        if (mPermissions != null) {
-            return mPermissions.keySet();
-        }
-
-        return Collections.emptySet();
+        return permissionData != null && permissionData.isGranted(userId);
     }
 
     /**
@@ -280,25 +228,122 @@
      * @return The permissions or an empty set.
      */
     public Set<String> getPermissions(int userId) {
-        return getPermissionsInternal(FLAG_INSTALL_PERMISSIONS | FLAG_RUNTIME_PERMISSIONS, userId);
+        enforceValidUserId(userId);
+
+        if (mPermissions == null) {
+            return Collections.emptySet();
+        }
+
+        Set<String> permissions = new ArraySet<>();
+
+        final int permissionCount = mPermissions.size();
+        for (int i = 0; i < permissionCount; i++) {
+            String permission = mPermissions.keyAt(i);
+
+            if (hasInstallPermission(permission)) {
+                permissions.add(permission);
+            }
+
+            if (userId != UserHandle.USER_ALL) {
+                if (hasRuntimePermission(permission, userId)) {
+                    permissions.add(permission);
+                }
+            }
+        }
+
+        return permissions;
     }
 
     /**
-     * Gets all runtime permissions.
+     * Gets the state for an install permission or null if no such.
      *
-     * @return The permissions or an empty set.
+     * @param name The permission name.
+     * @return The permission state.
      */
-    public Set<String> getRuntimePermissions(int userId) {
-        return getPermissionsInternal(FLAG_RUNTIME_PERMISSIONS, userId);
+    public PermissionState getInstallPermissionState(String name) {
+        return getPermissionState(name, UserHandle.USER_ALL);
     }
 
     /**
-     * Gets all install permissions.
+     * Gets the state for a runtime permission or null if no such.
      *
-     * @return The permissions or an empty set.
+     * @param name The permission name.
+     * @param userId The device user id.
+     * @return The permission state.
      */
-    public Set<String> getInstallPermissions() {
-        return getPermissionsInternal(FLAG_INSTALL_PERMISSIONS, UserHandle.USER_ALL);
+    public PermissionState getRuntimePermissionState(String name, int userId) {
+        enforceValidUserId(userId);
+        return getPermissionState(name, userId);
+    }
+
+    /**
+     * Gets all install permission states.
+     *
+     * @return The permission states or an empty set.
+     */
+    public List<PermissionState> getInstallPermissionStates() {
+        return getPermissionStatesInternal(UserHandle.USER_ALL);
+    }
+
+    /**
+     * Gets all runtime permission states.
+     *
+     * @return The permission states or an empty set.
+     */
+    public List<PermissionState> getRuntimePermissionStates(int userId) {
+        enforceValidUserId(userId);
+        return getPermissionStatesInternal(userId);
+    }
+
+    /**
+     * Gets the flags for a permission regardless if it is install or
+     * runtime permission.
+     *
+     * @param name The permission name.
+     * @return The permission state or null if no such.
+     */
+    public int getPermissionFlags(String name, int userId) {
+        PermissionState installPermState = getInstallPermissionState(name);
+        if (installPermState != null) {
+            return installPermState.getFlags();
+        }
+        PermissionState runtimePermState = getRuntimePermissionState(name, userId);
+        if (runtimePermState != null) {
+            return runtimePermState.getFlags();
+        }
+        return 0;
+    }
+
+    /**
+     * Update the flags associated with a given permission.
+     * @param permission The permission whose flags to update.
+     * @param userId The user for which to update.
+     * @param flagMask Mask for which flags to change.
+     * @param flagValues New values for the mask flags.
+     * @return Whether the permission flags changed.
+     */
+    public boolean updatePermissionFlags(BasePermission permission, int userId,
+            int flagMask, int flagValues) {
+        enforceValidUserId(userId);
+
+        final boolean mayChangeFlags = flagValues != 0 || flagMask != 0;
+
+        if (mPermissions == null) {
+            if (!mayChangeFlags) {
+                return false;
+            }
+            ensurePermissionData(permission);
+        }
+
+        PermissionData permissionData = mPermissions.get(permission.name);
+        if (permissionData == null) {
+            if (!mayChangeFlags) {
+                return false;
+            }
+            permissionData = ensurePermissionData(permission);
+        }
+
+        return permissionData.updateFlags(userId, flagMask, flagValues);
     }
 
     /**
@@ -357,36 +402,37 @@
         mPermissions = null;
     }
 
-    private Set<String> getPermissionsInternal(int flags, int userId) {
+    private PermissionState getPermissionState(String name, int userId) {
+        if (mPermissions == null) {
+            return null;
+        }
+        PermissionData permissionData = mPermissions.get(name);
+        if (permissionData == null) {
+            return null;
+        }
+        return permissionData.getPermissionState(userId);
+    }
+
+    private List<PermissionState> getPermissionStatesInternal(int userId) {
         enforceValidUserId(userId);
 
         if (mPermissions == null) {
-            return Collections.emptySet();
+            return Collections.emptyList();
         }
 
-        if (userId == UserHandle.USER_ALL) {
-            flags = FLAG_INSTALL_PERMISSIONS;
-        }
-
-        Set<String> permissions = new ArraySet<>();
+        List<PermissionState> permissionStates = new ArrayList<>();
 
         final int permissionCount = mPermissions.size();
         for (int i = 0; i < permissionCount; i++) {
-            String permission = mPermissions.keyAt(i);
+            PermissionData permissionData = mPermissions.valueAt(i);
 
-            if ((flags & FLAG_INSTALL_PERMISSIONS) != 0) {
-                if (hasInstallPermission(permission)) {
-                    permissions.add(permission);
-                }
-            }
-            if ((flags & FLAG_RUNTIME_PERMISSIONS) != 0) {
-                if (hasRuntimePermission(permission, userId)) {
-                    permissions.add(permission);
-                }
+            PermissionState permissionState = permissionData.getPermissionState(userId);
+            if (permissionState != null) {
+                permissionStates.add(permissionState);
             }
         }
 
-        return  permissions;
+        return  permissionStates;
     }
 
     private int grantPermission(BasePermission permission, int userId) {
@@ -397,17 +443,9 @@
         final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
         final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
 
-        if (mPermissions == null) {
-            mPermissions = new ArrayMap<>();
-        }
+        PermissionData permissionData = ensurePermissionData(permission);
 
-        PermissionData permissionData = mPermissions.get(permission.name);
-        if (permissionData == null) {
-            permissionData = new PermissionData(permission);
-            mPermissions.put(permission.name, permissionData);
-        }
-
-        if (!permissionData.addUserId(userId)) {
+        if (!permissionData.grant(userId)) {
             return PERMISSION_OPERATION_FAILURE;
         }
 
@@ -431,16 +469,12 @@
 
         PermissionData permissionData = mPermissions.get(permission.name);
 
-        if (!permissionData.removeUserId(userId)) {
+        if (!permissionData.revoke(userId)) {
             return PERMISSION_OPERATION_FAILURE;
         }
 
-        if (permissionData.getUserIds() == USERS_NONE) {
-            mPermissions.remove(permission.name);
-        }
-
-        if (mPermissions.isEmpty()) {
-            mPermissions = null;
+        if (permissionData.isDefault()) {
+            ensureNoPermissionData(permission.name);
         }
 
         if (hasGids) {
@@ -468,9 +502,31 @@
         }
     }
 
+    private PermissionData ensurePermissionData(BasePermission permission) {
+        if (mPermissions == null) {
+            mPermissions = new ArrayMap<>();
+        }
+        PermissionData permissionData = mPermissions.get(permission.name);
+        if (permissionData == null) {
+            permissionData = new PermissionData(permission);
+            mPermissions.put(permission.name, permissionData);
+        }
+        return permissionData;
+    }
+
+    private void ensureNoPermissionData(String name) {
+        if (mPermissions == null) {
+            return;
+        }
+        mPermissions.remove(name);
+        if (mPermissions.isEmpty()) {
+            mPermissions = null;
+        }
+    }
+
     private static final class PermissionData {
         private final BasePermission mPerm;
-        private int[] mUserIds = USERS_NONE;
+        private SparseArray<PermissionState> mUserStates = new SparseArray<>();
 
         public PermissionData(BasePermission perm) {
             mPerm = perm;
@@ -478,11 +534,11 @@
 
         public PermissionData(PermissionData other) {
             this(other.mPerm);
-
-            if (other.mUserIds == USERS_ALL || other.mUserIds == USERS_NONE) {
-                mUserIds = other.mUserIds;
-            } else {
-                mUserIds = Arrays.copyOf(other.mUserIds, other.mUserIds.length);
+            final int otherStateCount = other.mUserStates.size();
+            for (int i = 0; i < otherStateCount; i++) {
+                final int otherUserId = other.mUserStates.keyAt(i);
+                PermissionState otherState = other.mUserStates.valueAt(i);
+                mUserStates.put(otherUserId, new PermissionState(otherState));
             }
         }
 
@@ -490,53 +546,146 @@
             return mPerm.computeGids(userId);
         }
 
-        public int[] getUserIds() {
-            return mUserIds;
-        }
-
-        public boolean hasUserId(int userId) {
-            if (mUserIds == USERS_ALL) {
-                return true;
+        public boolean isGranted(int userId) {
+            if (isInstallPermission()) {
+                userId = UserHandle.USER_ALL;
             }
 
-            if (userId != UserHandle.USER_ALL) {
-                return ArrayUtils.contains(mUserIds, userId);
+            PermissionState userState = mUserStates.get(userId);
+            if (userState == null) {
+                return false;
+            }
+
+            return userState.mGranted;
+        }
+
+        public boolean grant(int userId) {
+            if (!isCompatibleUserId(userId)) {
+                return false;
+            }
+
+            if (isGranted(userId)) {
+                return false;
+            }
+
+            PermissionState userState = mUserStates.get(userId);
+            if (userState == null) {
+                userState = new PermissionState(mPerm.name);
+                mUserStates.put(userId, userState);
+            }
+
+            userState.mGranted = true;
+
+            return true;
+        }
+
+        public boolean revoke(int userId) {
+            if (!isCompatibleUserId(userId)) {
+                return false;
+            }
+
+            if (!isGranted(userId)) {
+                return false;
+            }
+
+            PermissionState userState = mUserStates.get(userId);
+            userState.mGranted = false;
+
+            if (userState.isDefault()) {
+                mUserStates.remove(userId);
+            }
+
+            return true;
+        }
+
+        public PermissionState getPermissionState(int userId) {
+            return mUserStates.get(userId);
+        }
+
+        public int getFlags(int userId) {
+            PermissionState userState = mUserStates.get(userId);
+            if (userState != null) {
+                return userState.mFlags;
+            }
+            return 0;
+        }
+
+        public boolean isDefault() {
+            return mUserStates.size() <= 0;
+        }
+
+        public static boolean isInstallPermissionKey(int userId) {
+            return userId == UserHandle.USER_ALL;
+        }
+
+        public boolean updateFlags(int userId, int flagMask, int flagValues) {
+            if (isInstallPermission()) {
+                userId = UserHandle.USER_ALL;
+            }
+
+            if (!isCompatibleUserId(userId)) {
+                return false;
+            }
+
+            final int newFlags = flagValues & flagMask;
+
+            PermissionState userState = mUserStates.get(userId);
+            if (userState != null) {
+                final int oldFlags = userState.mFlags;
+                userState.mFlags = (userState.mFlags & ~flagMask) | newFlags;
+                if (userState.isDefault()) {
+                    mUserStates.remove(userId);
+                }
+                return userState.mFlags != oldFlags;
+            } else if (newFlags != 0) {
+                userState = new PermissionState(mPerm.name);
+                userState.mFlags = newFlags;
+                mUserStates.put(userId, userState);
+                return true;
             }
 
             return false;
         }
 
-        public boolean addUserId(int userId) {
-            if (hasUserId(userId)) {
-                return false;
-            }
-
-            if (userId == UserHandle.USER_ALL) {
-                mUserIds = USERS_ALL;
-                return true;
-            }
-
-            mUserIds = ArrayUtils.appendInt(mUserIds, userId);
-
-            return true;
+        private boolean isCompatibleUserId(int userId) {
+            return isDefault() || !(isInstallPermission() ^ isInstallPermissionKey(userId));
         }
 
-        public boolean removeUserId(int userId) {
-            if (!hasUserId(userId)) {
-                return false;
-            }
+        private boolean isInstallPermission() {
+            return mUserStates.size() == 1
+                    && mUserStates.get(UserHandle.USER_ALL) != null;
+        }
+    }
 
-            if (mUserIds == USERS_ALL) {
-                mUserIds = UserManagerService.getInstance().getUserIds();
-            }
+    public static final class PermissionState {
+        private final String mName;
+        private boolean mGranted;
+        private int mFlags;
 
-            mUserIds = ArrayUtils.removeInt(mUserIds, userId);
+        public PermissionState(String name) {
+            mName = name;
+        }
 
-            if (mUserIds.length == 0) {
-                mUserIds = USERS_NONE;
-            }
+        public PermissionState(PermissionState other) {
+            mName = other.mName;
+            mGranted = other.mGranted;
+            mFlags = other.mFlags;
+        }
 
-            return true;
+        public boolean isDefault() {
+            return !mGranted && mFlags == 0;
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        public boolean isGranted() {
+            return mGranted;
+        }
+
+        public int getFlags() {
+            return mFlags;
         }
     }
 }
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 0c7f79d..c35258a 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -21,11 +21,13 @@
 import java.util.Arrays;
 
 abstract class SettingBase {
+    private static final int[] USERS_NONE = new int[0];
+
     int pkgFlags;
     int pkgPrivateFlags;
 
     protected final PermissionsState mPermissionsState;
-    private int[] mPermissionsUpdatedForUserIds = PermissionsState.USERS_NONE;
+    private int[] mPermissionsUpdatedForUserIds = USERS_NONE;
 
     SettingBase(int pkgFlags, int pkgPrivateFlags) {
         setFlags(pkgFlags);
@@ -53,7 +55,7 @@
             return;
         }
 
-        if (userIds == PermissionsState.USERS_NONE || userIds == PermissionsState.USERS_ALL) {
+        if (userIds == USERS_NONE) {
             mPermissionsUpdatedForUserIds = userIds;
         } else {
             mPermissionsUpdatedForUserIds = Arrays.copyOf(userIds, userIds.length);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index fd70ce1..2e9656a 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -60,6 +60,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.server.backup.PreferredActivityBackupHelper;
 import com.android.server.pm.PackageManagerService.DumpState;
+import com.android.server.pm.PermissionsState.PermissionState;
 
 import java.io.FileNotFoundException;
 import java.util.Collection;
@@ -178,6 +179,8 @@
     private static final String ATTR_CODE = "code";
     private static final String ATTR_NOT_LAUNCHED = "nl";
     private static final String ATTR_ENABLED = "enabled";
+    private static final String ATTR_GRANTED = "granted";
+    private static final String ATTR_FLAGS = "flags";
     private static final String ATTR_ENABLED_CALLER = "enabledCaller";
     private static final String ATTR_STOPPED = "stopped";
     // Legacy, here for reading older versions of the package-restrictions.
@@ -820,14 +823,20 @@
             }
 
             if (!used) {
+                PermissionsState permissionsState = sus.getPermissionsState();
+
                 // Try to revoke as an install permission which is for all users.
-                if (sus.getPermissionsState().revokeInstallPermission(bp) ==
+                // The package is gone - no need to keep flags for applying policy.
+                permissionsState.updatePermissionFlags(bp, userId,
+                        PackageManager.MASK_PERMISSION_FLAGS, 0);
+
+                if (permissionsState.revokeInstallPermission(bp) ==
                         PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
                     return UserHandle.USER_ALL;
                 }
 
                 // Try to revoke as an install permission which is per user.
-                if (sus.getPermissionsState().revokeRuntimePermission(bp, userId) ==
+                if (permissionsState.revokeRuntimePermission(bp, userId) ==
                         PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
                     return userId;
                 }
@@ -1724,10 +1733,32 @@
                     continue;
                 }
 
-                if (permissionsState.grantInstallPermission(bp) ==
-                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                    Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
-                    XmlUtils.skipCurrentTag(parser);
+                String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED);
+                final boolean granted = grantedStr == null
+                        || Boolean.parseBoolean(grantedStr);
+
+                String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS);
+                final int flags = (flagsStr != null)
+                        ? Integer.parseInt(flagsStr, 16) : 0;
+
+                if (granted) {
+                    if (permissionsState.grantInstallPermission(bp) ==
+                            PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                        Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
+                        XmlUtils.skipCurrentTag(parser);
+                    } else {
+                        permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
+                                PackageManager.MASK_PERMISSION_FLAGS, flags);
+                    }
+                } else {
+                    if (permissionsState.revokeInstallPermission(bp) ==
+                            PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                        Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
+                        XmlUtils.skipCurrentTag(parser);
+                    } else {
+                        permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
+                                PackageManager.MASK_PERMISSION_FLAGS, flags);
+                    }
                 }
             } else {
                 Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: "
@@ -1737,17 +1768,19 @@
         }
     }
 
-    void writePermissionsLPr(XmlSerializer serializer, Set<String> permissions)
+    void writePermissionsLPr(XmlSerializer serializer, List<PermissionState> permissionStates)
             throws IOException {
-        if (permissions.isEmpty()) {
+        if (permissionStates.isEmpty()) {
             return;
         }
 
         serializer.startTag(null, TAG_PERMISSIONS);
 
-        for (String permission : permissions) {
+        for (PermissionState permissionState : permissionStates) {
             serializer.startTag(null, TAG_ITEM);
-            serializer.attribute(null, ATTR_NAME, permission);
+            serializer.attribute(null, ATTR_NAME, permissionState.getName());
+            serializer.attribute(null, ATTR_GRANTED, String.valueOf(permissionState.isGranted()));
+            serializer.attribute(null, ATTR_FLAGS, Integer.toHexString(permissionState.getFlags()));
             serializer.endTag(null, TAG_ITEM);
         }
 
@@ -1945,7 +1978,8 @@
                 serializer.attribute(null, "userId",
                         Integer.toString(usr.userId));
                 usr.signatures.writeXml(serializer, "sigs", mPastSignatures);
-                writePermissionsLPr(serializer, usr.getPermissionsState().getInstallPermissions());
+                writePermissionsLPr(serializer, usr.getPermissionsState()
+                        .getInstallPermissionStates());
                 serializer.endTag(null, "shared-user");
             }
 
@@ -2120,7 +2154,8 @@
 
         // If this is a shared user, the permissions will be written there.
         if (pkg.sharedUser == null) {
-            writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions());
+            writePermissionsLPr(serializer, pkg.getPermissionsState()
+                    .getInstallPermissionStates());
         }
 
         serializer.endTag(null, "updated-package");
@@ -2175,9 +2210,9 @@
             serializer.attribute(null, "volumeUuid", pkg.volumeUuid);
         }
         pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
-        if ((pkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-            writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions());
-        }
+
+        writePermissionsLPr(serializer, pkg.getPermissionsState()
+                    .getInstallPermissionStates());
 
         writeSigningKeySetLPr(serializer, pkg.keySetData);
         writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
@@ -3922,7 +3957,7 @@
                 PermissionsState permissionsState = ps.getPermissionsState();
                 dumpGidsLPr(pw, prefix + "    ", permissionsState.computeGids(user.id));
                 dumpRuntimePermissionsLPr(pw, prefix + "    ", permissionsState
-                        .getRuntimePermissions(user.id));
+                        .getRuntimePermissionStates(user.id));
             }
 
             ArraySet<String> cmp = ps.getDisabledComponents(user.id);
@@ -4071,7 +4106,8 @@
 
                 for (int userId : UserManagerService.getInstance().getUserIds()) {
                     final int[] gids = permissionsState.computeGids(userId);
-                    Set<String> permissions = permissionsState.getRuntimePermissions(userId);
+                    List<PermissionState> permissions = permissionsState
+                            .getRuntimePermissionStates(userId);
                     if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) {
                         pw.print(prefix); pw.print("User "); pw.print(userId); pw.println(": ");
                         dumpGidsLPr(pw, prefix + "  ", gids);
@@ -4120,22 +4156,29 @@
         }
     }
 
-    void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, Set<String> permissions) {
-        if (!permissions.isEmpty()) {
+    void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix,
+            List<PermissionState> permissionStates) {
+        if (!permissionStates.isEmpty()) {
             pw.print(prefix); pw.println("runtime permissions:");
-            for (String permission : permissions) {
-                pw.print(prefix); pw.print("  "); pw.println(permission);
+            for (PermissionState permissionState : permissionStates) {
+                pw.print(prefix); pw.print("  "); pw.print(permissionState.getName());
+                pw.print(", granted="); pw.print(permissionState.isGranted());
+                    pw.print(", flags=0x"); pw.println(Integer.toHexString(
+                        permissionState.getFlags()));
             }
         }
     }
 
     void dumpInstallPermissionsLPr(PrintWriter pw, String prefix,
             PermissionsState permissionsState) {
-        Set<String> permissions = permissionsState.getInstallPermissions();
-        if (!permissions.isEmpty()) {
+        List<PermissionState> permissionStates = permissionsState.getInstallPermissionStates();
+        if (!permissionStates.isEmpty()) {
             pw.print(prefix); pw.println("install permissions:");
-            for (String permission : permissions) {
-                pw.print(prefix); pw.print("  "); pw.println(permission);
+            for (PermissionState permissionState : permissionStates) {
+                pw.print(prefix); pw.print("  "); pw.print(permissionState.getName());
+                    pw.print(", granted="); pw.print(permissionState.isGranted());
+                    pw.print(", flags=0x"); pw.println(Integer.toHexString(
+                        permissionState.getFlags()));
             }
         }
     }
@@ -4207,8 +4250,8 @@
         private void writePermissionsSync(int userId) {
             AtomicFile destination = new AtomicFile(getUserRuntimePermissionsFile(userId));
 
-            ArrayMap<String, Set<String>> permissionsForPackage = new ArrayMap<>();
-            ArrayMap<String, Set<String>> permissionsForSharedUser = new ArrayMap<>();
+            ArrayMap<String, List<PermissionState>> permissionsForPackage = new ArrayMap<>();
+            ArrayMap<String, List<PermissionState>> permissionsForSharedUser = new ArrayMap<>();
 
             synchronized (mLock) {
                 mWriteScheduled.delete(userId);
@@ -4219,9 +4262,10 @@
                     PackageSetting packageSetting = mPackages.valueAt(i);
                     if (packageSetting.sharedUser == null) {
                         PermissionsState permissionsState = packageSetting.getPermissionsState();
-                        Set<String> permissions = permissionsState.getRuntimePermissions(userId);
-                        if (!permissions.isEmpty()) {
-                            permissionsForPackage.put(packageName, permissions);
+                        List<PermissionState> permissionsStates = permissionsState
+                                .getRuntimePermissionStates(userId);
+                        if (!permissionsStates.isEmpty()) {
+                            permissionsForPackage.put(packageName, permissionsStates);
                         }
                     }
                 }
@@ -4231,9 +4275,10 @@
                     String sharedUserName = mSharedUsers.keyAt(i);
                     SharedUserSetting sharedUser = mSharedUsers.valueAt(i);
                     PermissionsState permissionsState = sharedUser.getPermissionsState();
-                    Set<String> permissions = permissionsState.getRuntimePermissions(userId);
-                    if (!permissions.isEmpty()) {
-                        permissionsForSharedUser.put(sharedUserName, permissions);
+                    List<PermissionState> permissionsStates = permissionsState
+                            .getRuntimePermissionStates(userId);
+                    if (!permissionsStates.isEmpty()) {
+                        permissionsForSharedUser.put(sharedUserName, permissionsStates);
                     }
                 }
             }
@@ -4252,20 +4297,20 @@
                 final int packageCount = permissionsForPackage.size();
                 for (int i = 0; i < packageCount; i++) {
                     String packageName = permissionsForPackage.keyAt(i);
-                    Set<String> permissions = permissionsForPackage.valueAt(i);
+                    List<PermissionState> permissionStates = permissionsForPackage.valueAt(i);
                     serializer.startTag(null, TAG_PACKAGE);
                     serializer.attribute(null, ATTR_NAME, packageName);
-                    writePermissions(serializer, permissions);
+                    writePermissions(serializer, permissionStates);
                     serializer.endTag(null, TAG_PACKAGE);
                 }
 
                 final int sharedUserCount = permissionsForSharedUser.size();
                 for (int i = 0; i < sharedUserCount; i++) {
                     String packageName = permissionsForSharedUser.keyAt(i);
-                    Set<String> permissions = permissionsForSharedUser.valueAt(i);
+                    List<PermissionState> permissionStates = permissionsForSharedUser.valueAt(i);
                     serializer.startTag(null, TAG_SHARED_USER);
                     serializer.attribute(null, ATTR_NAME, packageName);
-                    writePermissions(serializer, permissions);
+                    writePermissions(serializer, permissionStates);
                     serializer.endTag(null, TAG_SHARED_USER);
                 }
 
@@ -4290,20 +4335,23 @@
             mHandler.removeMessages(userId);
 
             for (SettingBase sb : mPackages.values()) {
-                revokeRuntimePermissions(sb, userId);
+                revokeRuntimePermissionsAndClearFlags(sb, userId);
             }
 
             for (SettingBase sb : mSharedUsers.values()) {
-                revokeRuntimePermissions(sb, userId);
+                revokeRuntimePermissionsAndClearFlags(sb, userId);
             }
         }
 
-        private void revokeRuntimePermissions(SettingBase sb, int userId) {
+        private void revokeRuntimePermissionsAndClearFlags(SettingBase sb, int userId) {
             PermissionsState permissionsState = sb.getPermissionsState();
-            for (String permission : permissionsState.getRuntimePermissions(userId)) {
-                BasePermission bp = mPermissions.get(permission);
+            for (PermissionState permissionState
+                    : permissionsState.getRuntimePermissionStates(userId)) {
+                BasePermission bp = mPermissions.get(permissionState.getName());
                 if (bp != null) {
                     permissionsState.revokeRuntimePermission(bp, userId);
+                    permissionsState.updatePermissionFlags(bp, userId,
+                            PackageManager.MASK_PERMISSION_FLAGS, 0);
                 }
             }
         }
@@ -4391,20 +4439,47 @@
                             continue;
                         }
 
-                        if (permissionsState.grantRuntimePermission(bp, userId) ==
-                                PermissionsState.PERMISSION_OPERATION_FAILURE) {
-                            Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name);
+                        String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED);
+                        final boolean granted = grantedStr == null
+                                || Boolean.parseBoolean(grantedStr);
+
+                        String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS);
+                        final int flags = (flagsStr != null)
+                                ? Integer.parseInt(flagsStr, 16) : 0;
+
+                        if (granted) {
+                            if (permissionsState.grantRuntimePermission(bp, userId) ==
+                                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                                Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name);
+                            } else {
+                                permissionsState.updatePermissionFlags(bp, userId,
+                                        PackageManager.MASK_PERMISSION_FLAGS, flags);
+
+                            }
+                        } else {
+                            if (permissionsState.revokeRuntimePermission(bp, userId) ==
+                                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                                Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name);
+                            } else {
+                                permissionsState.updatePermissionFlags(bp, userId,
+                                        PackageManager.MASK_PERMISSION_FLAGS, flags);
+                            }
                         }
+
                     } break;
                 }
             }
         }
 
-        private void writePermissions(XmlSerializer serializer, Set<String> permissions)
-                throws IOException {
-            for (String permission : permissions) {
+        private void writePermissions(XmlSerializer serializer,
+                List<PermissionState> permissionStates) throws IOException {
+            for (PermissionState permissionState : permissionStates) {
                 serializer.startTag(null, TAG_ITEM);
-                serializer.attribute(null, ATTR_NAME, permission);
+                serializer.attribute(null, ATTR_NAME,permissionState.getName());
+                serializer.attribute(null, ATTR_GRANTED,
+                        String.valueOf(permissionState.isGranted()));
+                serializer.attribute(null, ATTR_FLAGS,
+                        Integer.toHexString(permissionState.getFlags()));
                 serializer.endTag(null, TAG_ITEM);
             }
         }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8dccfdf..e463fad 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -241,7 +241,7 @@
                 }
             }
         }
-        userForeground(UserHandle.USER_OWNER);
+        onUserForeground(UserHandle.USER_OWNER);
         mAppOpsService = IAppOpsService.Stub.asInterface(
                 ServiceManager.getService(Context.APP_OPS_SERVICE));
         for (int i = 0; i < mUserIds.length; ++i) {
@@ -1784,7 +1784,7 @@
      * Make a note of the last started time of a user and do some cleanup.
      * @param userId the user that was just foregrounded
      */
-    public void userForeground(int userId) {
+    public void onUserForeground(int userId) {
         synchronized (mPackagesLock) {
             UserInfo user = mUsers.get(userId);
             long now = System.currentTimeMillis();
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index f790f75..74df0a0 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -69,6 +69,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 
 import libcore.util.Objects;
 
@@ -149,6 +150,12 @@
     private static final int POWER_HINT_INTERACTION = 2;
     private static final int POWER_HINT_LOW_POWER = 5;
 
+    // Power features defined in hardware/libhardware/include/hardware/power.h.
+    private static final int POWER_FEATURE_DOUBLE_TAP_TO_WAKE = 1;
+
+    // Default setting for double tap to wake.
+    private static final int DEFAULT_DOUBLE_TAP_TO_WAKE = 0;
+
     private final Context mContext;
     private final ServiceThread mHandlerThread;
     private final PowerManagerHandler mHandler;
@@ -338,6 +345,9 @@
     // Otherwise the user won't get much screen on time before dimming occurs.
     private float mMaximumScreenDimRatioConfig;
 
+    // Whether device supports double tap to wake.
+    private boolean mSupportsDoubleTapWakeConfig;
+
     // The screen off timeout setting value in milliseconds.
     private int mScreenOffTimeoutSetting;
 
@@ -423,9 +433,15 @@
     // True if we are currently in device idle mode.
     private boolean mDeviceIdleMode;
 
+    // Set of app ids that we will always respect the wake locks for.
+    int[] mDeviceIdleWhitelist = new int[0];
+
     // True if theater mode is enabled
     private boolean mTheaterModeEnabled;
 
+    // True if double tap to wake is enabled
+    private boolean mDoubleTapWakeEnabled;
+
     private final ArrayList<PowerManagerInternal.LowPowerModeListener> mLowPowerModeListeners
             = new ArrayList<PowerManagerInternal.LowPowerModeListener>();
 
@@ -436,6 +452,7 @@
     private static native void nativeSetInteractive(boolean enable);
     private static native void nativeSetAutoSuspend(boolean enable);
     private static native void nativeSendPowerHint(int hintId, int data);
+    private static native void nativeSetFeature(int featureId, int data);
 
     public PowerManagerService(Context context) {
         super(context);
@@ -458,6 +475,7 @@
             nativeInit();
             nativeSetAutoSuspend(false);
             nativeSetInteractive(true);
+            nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, 0);
         }
     }
 
@@ -576,6 +594,9 @@
             resolver.registerContentObserver(Settings.Global.getUriFor(
                     Settings.Global.THEATER_MODE_ON),
                     false, mSettingsObserver, UserHandle.USER_ALL);
+            resolver.registerContentObserver(Settings.Secure.getUriFor(
+                    Settings.Secure.DOUBLE_TAP_TO_WAKE),
+                    false, mSettingsObserver, UserHandle.USER_ALL);
             // Go.
             readConfigurationLocked();
             updateSettingsLocked();
@@ -621,6 +642,8 @@
                 com.android.internal.R.integer.config_maximumScreenDimDuration);
         mMaximumScreenDimRatioConfig = resources.getFraction(
                 com.android.internal.R.fraction.config_maximumScreenDimRatio, 1, 1);
+        mSupportsDoubleTapWakeConfig = resources.getBoolean(
+                com.android.internal.R.bool.config_supportDoubleTapWake);
     }
 
     private void updateSettingsLocked() {
@@ -649,6 +672,16 @@
         mTheaterModeEnabled = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.THEATER_MODE_ON, 0) == 1;
 
+        if (mSupportsDoubleTapWakeConfig) {
+            boolean doubleTapWakeEnabled = Settings.Secure.getIntForUser(resolver,
+                    Settings.Secure.DOUBLE_TAP_TO_WAKE, DEFAULT_DOUBLE_TAP_TO_WAKE,
+                            UserHandle.USER_CURRENT) != 0;
+            if (doubleTapWakeEnabled != mDoubleTapWakeEnabled) {
+                mDoubleTapWakeEnabled = doubleTapWakeEnabled;
+                nativeSetFeature(POWER_FEATURE_DOUBLE_TAP_TO_WAKE, mDoubleTapWakeEnabled ? 1 : 0);
+            }
+        }
+
         final int oldScreenBrightnessSetting = mScreenBrightnessSetting;
         mScreenBrightnessSetting = Settings.System.getIntForUser(resolver,
                 Settings.System.SCREEN_BRIGHTNESS, mScreenBrightnessSettingDefault,
@@ -758,6 +791,7 @@
                     throw new IllegalArgumentException("Wake lock is already dead.");
                 }
                 mWakeLocks.add(wakeLock);
+                setWakeLockDisabledStateLocked(wakeLock);
                 notifyAcquire = true;
             }
 
@@ -894,7 +928,7 @@
     }
 
     private void notifyWakeLockAcquiredLocked(WakeLock wakeLock) {
-        if (mSystemReady) {
+        if (mSystemReady && !wakeLock.mDisabled) {
             wakeLock.mNotifiedAcquired = true;
             mNotifier.onWakeLockAcquired(wakeLock.mFlags, wakeLock.mTag, wakeLock.mPackageName,
                     wakeLock.mOwnerUid, wakeLock.mOwnerPid, wakeLock.mWorkSource,
@@ -1388,7 +1422,10 @@
                 final WakeLock wakeLock = mWakeLocks.get(i);
                 switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
                     case PowerManager.PARTIAL_WAKE_LOCK:
-                        mWakeLockSummary |= WAKE_LOCK_CPU;
+                        if (!wakeLock.mDisabled) {
+                            // We only respect this if the wake lock is not disabled.
+                            mWakeLockSummary |= WAKE_LOCK_CPU;
+                        }
                         break;
                     case PowerManager.FULL_WAKE_LOCK:
                         mWakeLockSummary |= WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT;
@@ -2248,12 +2285,12 @@
         }
     }
 
-    private void setStayOnSettingInternal(int val) {
+    void setStayOnSettingInternal(int val) {
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.STAY_ON_WHILE_PLUGGED_IN, val);
     }
 
-    private void setMaximumScreenOffTimeoutFromDeviceAdminInternal(int timeMs) {
+    void setMaximumScreenOffTimeoutFromDeviceAdminInternal(int timeMs) {
         synchronized (mLock) {
             mMaximumScreenOffTimeoutFromDeviceAdmin = timeMs;
             mDirty |= DIRTY_SETTINGS;
@@ -2261,6 +2298,69 @@
         }
     }
 
+    void setDeviceIdleModeInternal(boolean enabled) {
+        synchronized (mLock) {
+            if (mDeviceIdleMode != enabled) {
+                mDeviceIdleMode = enabled;
+                updateWakeLockDisabledStatesLocked();
+            }
+        }
+    }
+
+    void setDeviceIdleWhitelistInternal(int[] appids) {
+        synchronized (mLock) {
+            mDeviceIdleWhitelist = appids;
+            if (mDeviceIdleMode) {
+                updateWakeLockDisabledStatesLocked();
+            }
+        }
+    }
+
+    private void updateWakeLockDisabledStatesLocked() {
+        boolean changed = false;
+        final int numWakeLocks = mWakeLocks.size();
+        for (int i = 0; i < numWakeLocks; i++) {
+            final WakeLock wakeLock = mWakeLocks.get(i);
+            if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
+                    == PowerManager.PARTIAL_WAKE_LOCK) {
+                if (setWakeLockDisabledStateLocked(wakeLock)) {
+                    changed = true;
+                    if (wakeLock.mDisabled) {
+                        // This wake lock is no longer being respected.
+                        notifyWakeLockReleasedLocked(wakeLock);
+                    } else {
+                        notifyWakeLockAcquiredLocked(wakeLock);
+                    }
+                }
+            }
+        }
+        if (changed) {
+            mDirty |= DIRTY_WAKE_LOCKS;
+            updatePowerStateLocked();
+        }
+    }
+
+    private boolean setWakeLockDisabledStateLocked(WakeLock wakeLock) {
+        if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
+                == PowerManager.PARTIAL_WAKE_LOCK) {
+            boolean disabled = false;
+            if (mDeviceIdleMode) {
+                final int appid = UserHandle.getAppId(wakeLock.mOwnerUid);
+                // If we are in idle mode, we will ignore all partial wake locks that are
+                // for application uids that are not whitelisted.
+                if (appid >= Process.FIRST_APPLICATION_UID &&
+                        Arrays.binarySearch(mDeviceIdleWhitelist, appid) < 0) {
+                    disabled = true;
+                }
+            }
+            if (wakeLock.mDisabled != disabled) {
+                wakeLock.mDisabled = disabled;
+                return true;
+            }
+        }
+        return false;
+    }
+
     private boolean isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked() {
         return mMaximumScreenOffTimeoutFromDeviceAdmin >= 0
                 && mMaximumScreenOffTimeoutFromDeviceAdmin < Integer.MAX_VALUE;
@@ -2459,6 +2559,8 @@
             pw.println("  mSandmanSummoned=" + mSandmanSummoned);
             pw.println("  mLowPowerModeEnabled=" + mLowPowerModeEnabled);
             pw.println("  mBatteryLevelLow=" + mBatteryLevelLow);
+            pw.println("  mDeviceIdleMode=" + mDeviceIdleMode);
+            pw.println("  mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist));
             pw.println("  mLastWakeTime=" + TimeUtils.formatUptime(mLastWakeTime));
             pw.println("  mLastSleepTime=" + TimeUtils.formatUptime(mLastSleepTime));
             pw.println("  mLastUserActivityTime=" + TimeUtils.formatUptime(mLastUserActivityTime));
@@ -2537,6 +2639,7 @@
             pw.println("  mScreenBrightnessSettingMinimum=" + mScreenBrightnessSettingMinimum);
             pw.println("  mScreenBrightnessSettingMaximum=" + mScreenBrightnessSettingMaximum);
             pw.println("  mScreenBrightnessSettingDefault=" + mScreenBrightnessSettingDefault);
+            pw.println("  mDoubleTapWakeEnabled=" + mDoubleTapWakeEnabled);
 
             final int sleepTimeout = getSleepTimeoutLocked();
             final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
@@ -2671,6 +2774,7 @@
         public final int mOwnerUid;
         public final int mOwnerPid;
         public boolean mNotifiedAcquired;
+        public boolean mDisabled;
 
         public WakeLock(IBinder lock, int flags, String tag, String packageName,
                 WorkSource workSource, String historyTag, int ownerUid, int ownerPid) {
@@ -2729,7 +2833,7 @@
         @Override
         public String toString() {
             return getLockLevelString()
-                    + " '" + mTag + "'" + getLockFlagsString()
+                    + " '" + mTag + "'" + getLockFlagsString() + (mDisabled ? " DISABLED" : "")
                     + " (uid=" + mOwnerUid + ", pid=" + mOwnerPid + ", ws=" + mWorkSource + ")";
         }
 
@@ -3340,9 +3444,12 @@
 
         @Override
         public void setDeviceIdleMode(boolean enabled) {
-            synchronized (mLock) {
-                mDeviceIdleMode = enabled;
-            }
+            setDeviceIdleModeInternal(enabled);
+        }
+
+        @Override
+        public void setDeviceIdleWhitelist(int[] appids) {
+            setDeviceIdleWhitelistInternal(appids);
         }
     }
 }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 56816f9..e649e48 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1206,7 +1206,7 @@
         }
 
         @Override
-        public void requestUnblockContent(
+        public void unblockContent(
                 IBinder sessionToken, String unblockedRating, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
@@ -1216,9 +1216,9 @@
                 synchronized (mLock) {
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId)
-                                .requestUnblockContent(unblockedRating);
+                                .unblockContent(unblockedRating);
                     } catch (RemoteException | SessionNotFoundException e) {
-                        Slog.e(TAG, "error in requestUnblockContent", e);
+                        Slog.e(TAG, "error in unblockContent", e);
                     }
                 }
             } finally {
diff --git a/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp
index 7dbfaf6..39474ec 100644
--- a/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp
+++ b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp
@@ -181,6 +181,21 @@
     return gContext.device->get_authenticator_id(gContext.device);
 }
 
+static jint nativeSetActiveGroup(JNIEnv *env, jobject clazz, jint gid, jbyteArray path) {
+    const int pathSize = env->GetArrayLength(path);
+    jbyte* pathData = env->GetByteArrayElements(path, 0);
+    if (pathSize >= PATH_MAX) {
+	ALOGE("Path name is too long\n");
+        return -1;
+    }
+    char path_name[PATH_MAX] = {0};
+    memcpy(path_name, pathData, pathSize);
+    ALOG(LOG_VERBOSE, LOG_TAG, "nativeSetActiveGroup() path: %s, gid: %d\n", path_name, gid);
+    int result = gContext.device->set_active_group(gContext.device, gid, path_name);
+    env->ReleaseByteArrayElements(path, pathData, 0);
+    return result;
+}
+
 static jint nativeOpenHal(JNIEnv* env, jobject clazz) {
     ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n");
     int err;
@@ -242,6 +257,7 @@
     { "nativeAuthenticate", "(JI)I", (void*)nativeAuthenticate },
     { "nativeStopAuthentication", "()I", (void*)nativeStopAuthentication },
     { "nativeEnroll", "([BII)I", (void*)nativeEnroll },
+    { "nativeSetActiveGroup", "(I[B)I", (void*)nativeSetActiveGroup },
     { "nativePreEnroll", "()J", (void*)nativePreEnroll },
     { "nativeStopEnrollment", "()I", (void*)nativeStopEnrollment },
     { "nativeRemove", "(II)I", (void*)nativeRemove },
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 6dcdd9d..1662755 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -156,6 +156,14 @@
     }
 }
 
+static void nativeSetFeature(JNIEnv *env, jclass clazz, jint featureId, jint data) {
+    int data_param = data;
+
+    if (gPowerModule && gPowerModule->setFeature) {
+        gPowerModule->setFeature(gPowerModule, (feature_t)featureId, data_param);
+    }
+}
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gPowerManagerServiceMethods[] = {
@@ -172,6 +180,8 @@
             (void*) nativeSetAutoSuspend },
     { "nativeSendPowerHint", "(II)V",
             (void*) nativeSendPowerHint },
+    { "nativeSetFeature", "(II)V",
+            (void*) nativeSetFeature },
 };
 
 #define FIND_CLASS(var, className) \
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
index 28ffc57..88bf54e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceOwner.java
@@ -17,6 +17,7 @@
 package com.android.server.devicepolicy;
 
 import android.app.AppGlobals;
+import android.app.admin.SystemUpdatePolicy;
 import android.content.ComponentName;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -78,7 +79,7 @@
     private final HashMap<Integer, OwnerInfo> mProfileOwners = new HashMap<Integer, OwnerInfo>();
 
     // Local system update policy controllable by device owner.
-    private PersistableBundle mSystemUpdatePolicy;
+    private SystemUpdatePolicy mSystemUpdatePolicy;
 
     // Private default constructor.
     private DeviceOwner() {
@@ -192,11 +193,11 @@
         return mProfileOwners.keySet();
     }
 
-    PersistableBundle getSystemUpdatePolicy() {
+    SystemUpdatePolicy getSystemUpdatePolicy() {
         return mSystemUpdatePolicy;
     }
 
-    void setSystemUpdatePolicy(PersistableBundle systemUpdatePolicy) {
+    void setSystemUpdatePolicy(SystemUpdatePolicy systemUpdatePolicy) {
         mSystemUpdatePolicy = systemUpdatePolicy;
     }
 
@@ -291,7 +292,7 @@
                     }
                     mProfileOwners.put(userId, profileOwnerInfo);
                 } else if (TAG_SYSTEM_UPDATE_POLICY.equals(tag)) {
-                    mSystemUpdatePolicy = PersistableBundle.restoreFromXml(parser);
+                    mSystemUpdatePolicy = SystemUpdatePolicy.restoreFromXml(parser);
                 } else {
                     throw new XmlPullParserException(
                             "Unexpected tag in device owner file: " + tag);
@@ -361,11 +362,7 @@
             // Write system update policy tag
             if (mSystemUpdatePolicy != null) {
                 out.startTag(null, TAG_SYSTEM_UPDATE_POLICY);
-                try {
-                    mSystemUpdatePolicy.saveToXml(out);
-                } catch (XmlPullParserException e) {
-                    Slog.e(TAG, "Failed to save system update policy", e);
-                }
+                mSystemUpdatePolicy.saveToXml(out);
                 out.endTag(null, TAG_SYSTEM_UPDATE_POLICY);
             }
             out.endDocument();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 67c198f..ba5d666 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -39,6 +39,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.IDevicePolicyManager;
+import android.app.admin.SystemUpdatePolicy;
 import android.app.backup.IBackupManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -6238,7 +6239,10 @@
     }
 
     @Override
-    public void setSystemUpdatePolicy(ComponentName who, PersistableBundle policy) {
+    public void setSystemUpdatePolicy(ComponentName who, SystemUpdatePolicy policy) {
+        if (policy != null && !policy.isValid()) {
+            throw new IllegalArgumentException("Invalid system update policy.");
+        }
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
             if (policy == null) {
@@ -6254,9 +6258,14 @@
     }
 
     @Override
-    public PersistableBundle getSystemUpdatePolicy() {
+    public SystemUpdatePolicy getSystemUpdatePolicy() {
         synchronized (this) {
-            return mDeviceOwner.getSystemUpdatePolicy();
+            SystemUpdatePolicy policy =  mDeviceOwner.getSystemUpdatePolicy();
+            if (policy != null && !policy.isValid()) {
+                Slog.w(LOG_TAG, "Stored system update policy is invalid, return null instead.");
+                return null;
+            }
+            return policy;
         }
     }
 
@@ -6344,10 +6353,18 @@
             getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             long ident = Binder.clearCallingIdentity();
             try {
+                PackageManager packageManager = mContext.getPackageManager();
                 if (granted) {
-                    mContext.getPackageManager().grantPermission(packageName, permission, user);
+                    packageManager.grantRuntimePermission(packageName, permission, user);
+                    packageManager.updatePermissionFlags(permission, packageName,
+                            PackageManager.FLAG_PERMISSION_POLICY_FIXED,
+                            PackageManager.FLAG_PERMISSION_POLICY_FIXED, user);
                 } else {
-                    mContext.getPackageManager().revokePermission(packageName, permission, user);
+                    packageManager.revokeRuntimePermission(packageName,
+                            permission, user);
+                    packageManager.updatePermissionFlags(permission, packageName,
+                            PackageManager.FLAG_PERMISSION_POLICY_FIXED,
+                            PackageManager.FLAG_PERMISSION_POLICY_FIXED, user);
                 }
                 return true;
             } catch (SecurityException se) {
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 869d6e1..d6ff475 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -48,6 +48,7 @@
             usageStats.mPackageName = getCachedStringRef(packageName);
             usageStats.mBeginTimeStamp = beginTime;
             usageStats.mEndTimeStamp = endTime;
+            usageStats.mBeginIdleTime = endTime;
             packageStats.put(usageStats.mPackageName, usageStats);
         }
         return usageStats;
@@ -119,6 +120,17 @@
         endTime = timeStamp;
     }
 
+    /**
+     * Updates the last active time for the package. The timestamp uses a timebase that
+     * tracks the device usage time.
+     * @param packageName
+     * @param timeStamp
+     */
+    void updateBeginIdleTime(String packageName, long timeStamp) {
+        UsageStats usageStats = getOrCreateUsageStats(packageName);
+        usageStats.mBeginIdleTime = timeStamp;
+    }
+
     void updateConfigurationStats(Configuration config, long timeStamp) {
         if (activeConfiguration != null) {
             ConfigurationStats activeStats = configurations.get(activeConfiguration);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 48b127a..197daed 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -40,31 +40,39 @@
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
 import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.IDeviceIdleController;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.util.ArraySet;
+import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.Display;
 
-import com.android.internal.appwidget.IAppWidgetService;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.SystemConfig;
+import com.android.server.DeviceIdleController;
 import com.android.server.SystemService;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -76,6 +84,7 @@
  */
 public class UsageStatsService extends SystemService implements
         UserUsageStatsService.StatsUpdatedListener {
+
     static final String TAG = "UsageStatsService";
 
     static final boolean DEBUG = false;
@@ -91,7 +100,7 @@
     static final int MSG_FLUSH_TO_DISK = 1;
     static final int MSG_REMOVE_USER = 2;
     static final int MSG_INFORM_LISTENERS = 3;
-    static final int MSG_RESET_LAST_TIMESTAMP = 4;
+    static final int MSG_FORCE_IDLE_STATE = 4;
     static final int MSG_CHECK_IDLE_STATES = 5;
 
     private final Object mLock = new Object();
@@ -99,16 +108,20 @@
     AppOpsManager mAppOps;
     UserManager mUserManager;
     AppWidgetManager mAppWidgetManager;
+    IDeviceIdleController mDeviceIdleController;
+    private DisplayManager mDisplayManager;
 
     private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
     private File mUsageStatsDir;
     long mRealTimeSnapshot;
     long mSystemTimeSnapshot;
     boolean mAppIdleParoled;
+    private boolean mScreenOn;
 
     long mAppIdleDurationMillis;
-
     long mCheckIdleIntervalMillis = DEFAULT_CHECK_IDLE_INTERVAL;
+    long mScreenOnTime;
+    long mScreenOnSystemTimeSnapshot;
 
     private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
             mPackageAccessListeners = new ArrayList<>();
@@ -162,6 +175,18 @@
             // Observe changes to the threshold
             new SettingsObserver(mHandler).registerObserver();
             mAppWidgetManager = getContext().getSystemService(AppWidgetManager.class);
+            mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+                    ServiceManager.getService(DeviceIdleController.SERVICE_NAME));
+            mDisplayManager = (DisplayManager) getContext().getSystemService(
+                    Context.DISPLAY_SERVICE);
+            mScreenOnSystemTimeSnapshot = System.currentTimeMillis();
+            synchronized (this) {
+                mScreenOnTime = readScreenOnTimeLocked();
+            }
+            mDisplayManager.registerDisplayListener(mDisplayListener, null);
+            synchronized (this) {
+                updateDisplayLocked();
+            }
         } else if (phase == PHASE_BOOT_COMPLETED) {
             setAppIdleParoled(getContext().getSystemService(BatteryManager.class).isCharging());
         }
@@ -195,6 +220,24 @@
         }
     }
 
+    private final DisplayManager.DisplayListener mDisplayListener
+            = new DisplayManager.DisplayListener() {
+
+        @Override public void onDisplayAdded(int displayId) {
+        }
+
+        @Override public void onDisplayRemoved(int displayId) {
+        }
+
+        @Override public void onDisplayChanged(int displayId) {
+            if (displayId == Display.DEFAULT_DISPLAY) {
+                synchronized (UsageStatsService.this.mLock) {
+                    updateDisplayLocked();
+                }
+            }
+        }
+    };
+
     @Override
     public void onStatsUpdated() {
         mHandler.sendEmptyMessageDelayed(MSG_FLUSH_TO_DISK, FLUSH_INTERVAL);
@@ -261,7 +304,7 @@
                 final int packageCount = packages.size();
                 for (int p = 0; p < packageCount; p++) {
                     final String packageName = packages.get(p).packageName;
-                    final boolean isIdle = isAppIdle(packageName, userId);
+                    final boolean isIdle = isAppIdleFiltered(packageName, userId);
                     mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
                             userId, isIdle ? 1 : 0, packageName));
                 }
@@ -270,6 +313,61 @@
         mHandler.sendEmptyMessageDelayed(MSG_CHECK_IDLE_STATES, mCheckIdleIntervalMillis);
     }
 
+    void updateDisplayLocked() {
+        boolean screenOn = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState()
+                != Display.STATE_OFF;
+        if (screenOn == mScreenOn) return;
+
+        mScreenOn = screenOn;
+        long now = System.currentTimeMillis();
+        if (mScreenOn) {
+            mScreenOnSystemTimeSnapshot = now;
+        } else {
+            mScreenOnTime += now - mScreenOnSystemTimeSnapshot;
+            writeScreenOnTimeLocked(mScreenOnTime);
+        }
+    }
+
+    private long getScreenOnTimeLocked(long now) {
+        if (mScreenOn) {
+            return now - mScreenOnSystemTimeSnapshot + mScreenOnTime;
+        } else {
+            return mScreenOnTime;
+        }
+    }
+
+    private File getScreenOnTimeFile() {
+        return new File(mUsageStatsDir, UserHandle.USER_OWNER + "/screen_on_time");
+    }
+
+    private long readScreenOnTimeLocked() {
+        long screenOnTime = 0;
+        File screenOnTimeFile = getScreenOnTimeFile();
+        if (screenOnTimeFile.exists()) {
+            try {
+                BufferedReader reader = new BufferedReader(new FileReader(screenOnTimeFile));
+                screenOnTime = Long.parseLong(reader.readLine());
+                reader.close();
+            } catch (IOException | NumberFormatException e) {
+            }
+        } else {
+            writeScreenOnTimeLocked(screenOnTime);
+        }
+        return screenOnTime;
+    }
+
+    private void writeScreenOnTimeLocked(long screenOnTime) {
+        AtomicFile screenOnTimeFile = new AtomicFile(getScreenOnTimeFile());
+        FileOutputStream fos = null;
+        try {
+            fos = screenOnTimeFile.startWrite();
+            fos.write(Long.toString(screenOnTime).getBytes());
+            screenOnTimeFile.finishWrite(fos);
+        } catch (IOException ioe) {
+            screenOnTimeFile.failWrite(fos);
+        }
+    }
+
     private static void deleteRecursively(File f) {
         File[] files = f.listFiles();
         if (files != null) {
@@ -289,7 +387,7 @@
         if (service == null) {
             service = new UserUsageStatsService(getContext(), userId,
                     new File(mUsageStatsDir, Integer.toString(userId)), this);
-            service.init(currentTimeMillis);
+            service.init(currentTimeMillis, getScreenOnTimeLocked(currentTimeMillis));
             mUserState.put(userId, service);
         }
         return service;
@@ -343,9 +441,10 @@
 
             final UserUsageStatsService service =
                     getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            final long lastUsed = service.getLastPackageAccessTime(event.mPackage);
-            final boolean previouslyIdle = hasPassedIdleDuration(lastUsed);
-            service.reportEvent(event);
+            final long lastUsed = service.getBeginIdleTime(event.mPackage);
+            final long screenOnTime = getScreenOnTimeLocked(timeNow);
+            final boolean previouslyIdle = hasPassedIdleTimeout(lastUsed, screenOnTime);
+            service.reportEvent(event, screenOnTime);
             // Inform listeners if necessary
             if ((event.mEventType == Event.MOVE_TO_FOREGROUND
                     || event.mEventType == Event.MOVE_TO_BACKGROUND
@@ -360,19 +459,21 @@
     }
 
     /**
-     * Forces the app's timestamp to reflect idle or active. If idle, then it rolls back the
-     * last used timestamp to a point in time thats behind the threshold for idle.
+     * Forces the app's beginIdleTime to reflect idle or active. If idle, then it rolls back the
+     * beginIdleTime to a point in time thats behind the threshold for idle.
      */
-    void resetLastTimestamp(String packageName, int userId, boolean idle) {
+    void forceIdleState(String packageName, int userId, boolean idle) {
         synchronized (mLock) {
             final long timeNow = checkAndGetTimeLocked();
-            final long lastTimestamp = timeNow - (idle ? mAppIdleDurationMillis : 0) - 5000;
+            final long screenOnTime = getScreenOnTimeLocked(timeNow);
+            final long deviceUsageTime = screenOnTime - (idle ? mAppIdleDurationMillis : 0) - 5000;
 
             final UserUsageStatsService service =
                     getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            final long lastUsed = service.getLastPackageAccessTime(packageName);
-            final boolean previouslyIdle = hasPassedIdleDuration(lastUsed);
-            service.setLastTimestamp(packageName, lastTimestamp);
+            final long lastUsed = service.getBeginIdleTime(packageName);
+            final boolean previouslyIdle = hasPassedIdleTimeout(lastUsed,
+                    getScreenOnTimeLocked(timeNow));
+            service.setBeginIdleTime(packageName, deviceUsageTime);
             // Inform listeners if necessary
             if (previouslyIdle != idle) {
                 // Slog.d(TAG, "Informing listeners of out-of-idle " + event.mPackage);
@@ -451,23 +552,25 @@
         }
     }
 
-    /**
-     * Called by LocalService stub.
-     */
-    long getLastPackageAccessTime(String packageName, int userId) {
+    private boolean isAppIdleUnfiltered(String packageName, int userId) {
         synchronized (mLock) {
             final long timeNow = checkAndGetTimeLocked();
-            // android package is always considered non-idle.
-            // TODO: Add a generic whitelisting mechanism
-            if (packageName.equals("android")) {
-                return timeNow;
-            }
             final UserUsageStatsService service =
                     getUserDataAndInitializeIfNeededLocked(userId, timeNow);
-            return service.getLastPackageAccessTime(packageName);
+            long beginIdleTime = service.getBeginIdleTime(packageName);
+            return hasPassedIdleTimeout(beginIdleTime, getScreenOnTimeLocked(timeNow));
         }
     }
 
+    /**
+     * @param timestamp when the app was last used in device usage timebase
+     * @param currentTime current time in device usage timebase
+     * @return whether it's been used far enough in the past to be considered inactive
+     */
+    boolean hasPassedIdleTimeout(long timestamp, long currentTime) {
+        return timestamp <= currentTime - mAppIdleDurationMillis;
+    }
+
     void addListener(AppIdleStateChangeListener listener) {
         synchronized (mLock) {
             if (!mPackageAccessListeners.contains(listener)) {
@@ -482,12 +585,13 @@
         }
     }
 
-    private boolean hasPassedIdleDuration(long lastUsed) {
-        final long now = System.currentTimeMillis();
-        return lastUsed <= now - mAppIdleDurationMillis;
-    }
-
-    boolean isAppIdle(String packageName, int userId) {
+    /**
+     * Checks if an app has been idle for a while and filters out apps that are excluded.
+     * It returns false if the current system state allows all apps to be considered active.
+     * This happens if the device is plugged in or temporarily allowed to make exceptions.
+     * Called by interface impls.
+     */
+    boolean isAppIdleFiltered(String packageName, int userId) {
         if (packageName == null) return false;
         synchronized (mLock) {
             // Temporary exemption, probably due to device charging or occasional allowance to
@@ -496,8 +600,12 @@
                 return false;
             }
         }
-        if (SystemConfig.getInstance().getAllowInPowerSave().contains(packageName)) {
-            return false;
+        if (packageName.equals("android")) return false;
+        try {
+            if (mDeviceIdleController.isPowerSaveWhitelistApp(packageName)) {
+                return false;
+            }
+        } catch (RemoteException re) {
         }
         // TODO: Optimize this check
         if (isActiveDeviceAdmin(packageName, userId)) {
@@ -509,14 +617,13 @@
             return false;
         }
 
-        final long lastUsed = getLastPackageAccessTime(packageName, userId);
-        return hasPassedIdleDuration(lastUsed);
+        return isAppIdleUnfiltered(packageName, userId);
     }
 
     void setAppIdle(String packageName, boolean idle, int userId) {
         if (packageName == null) return;
 
-        mHandler.obtainMessage(MSG_RESET_LAST_TIMESTAMP, userId, idle ? 1 : 0, packageName)
+        mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
                 .sendToTarget();
     }
 
@@ -559,6 +666,7 @@
      */
     void dump(String[] args, PrintWriter pw) {
         synchronized (mLock) {
+            final long screenOnTime = getScreenOnTimeLocked(checkAndGetTimeLocked());
             IndentingPrintWriter idpw = new IndentingPrintWriter(pw, "  ");
             ArraySet<String> argSet = new ArraySet<>();
             argSet.addAll(Arrays.asList(args));
@@ -569,12 +677,13 @@
                 idpw.println();
                 idpw.increaseIndent();
                 if (argSet.contains("--checkin")) {
-                    mUserState.valueAt(i).checkin(idpw);
+                    mUserState.valueAt(i).checkin(idpw, screenOnTime);
                 } else {
-                    mUserState.valueAt(i).dump(idpw);
+                    mUserState.valueAt(i).dump(idpw, screenOnTime);
                 }
                 idpw.decreaseIndent();
             }
+            pw.write("Screen On Timestamp:" + mScreenOnTime + "\n");
         }
     }
 
@@ -602,8 +711,8 @@
                     informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
                     break;
 
-                case MSG_RESET_LAST_TIMESTAMP:
-                    resetLastTimestamp((String) msg.obj, msg.arg1, msg.arg2 == 1);
+                case MSG_FORCE_IDLE_STATE:
+                    forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
                     break;
 
                 case MSG_CHECK_IDLE_STATES:
@@ -727,7 +836,7 @@
             }
             final long token = Binder.clearCallingIdentity();
             try {
-                return UsageStatsService.this.isAppIdle(packageName, userId);
+                return UsageStatsService.this.isAppIdleFiltered(packageName, userId);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -832,12 +941,7 @@
 
         @Override
         public boolean isAppIdle(String packageName, int userId) {
-            return UsageStatsService.this.isAppIdle(packageName, userId);
-        }
-
-        @Override
-        public long getLastPackageAccessTime(String packageName, int userId) {
-            return UsageStatsService.this.getLastPackageAccessTime(packageName, userId);
+            return UsageStatsService.this.isAppIdleFiltered(packageName, userId);
         }
 
         @Override
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index bfb71c5..0111201 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -26,6 +26,7 @@
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
 import android.content.res.Configuration;
+import android.text.TextUtils;
 
 import java.io.IOException;
 import java.net.ProtocolException;
@@ -54,6 +55,7 @@
 
     // Time attributes stored as an offset of the beginTime.
     private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
+    private static final String BEGIN_IDLE_TIME_ATTR = "beginIdleTime";
     private static final String END_TIME_ATTR = "endTime";
     private static final String TIME_ATTR = "time";
 
@@ -69,7 +71,10 @@
         // Apply the offset to the beginTime to find the absolute time.
         stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
                 parser, LAST_TIME_ACTIVE_ATTR);
-
+        final String beginIdleTime = parser.getAttributeValue(null, BEGIN_IDLE_TIME_ATTR);
+        if (!TextUtils.isEmpty(beginIdleTime)) {
+            stats.mBeginIdleTime = Long.parseLong(beginIdleTime);
+        }
         stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
         stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
     }
@@ -129,6 +134,7 @@
         XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
         XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
         XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
+        XmlUtils.writeLongAttribute(xml, BEGIN_IDLE_TIME_ATTR, usageStats.mBeginIdleTime);
 
         xml.endTag(null, PACKAGE_TAG);
     }
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index d94759d..b638c8e 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -80,7 +80,7 @@
         mUserId = userId;
     }
 
-    void init(final long currentTimeMillis) {
+    void init(final long currentTimeMillis, final long deviceUsageTime) {
         mDatabase.init(currentTimeMillis);
 
         int nullCount = 0;
@@ -135,7 +135,8 @@
         }
 
         if (mDatabase.isNewUpdate()) {
-            initializeDefaultsForApps(currentTimeMillis, mDatabase.isFirstUpdate());
+            initializeDefaultsForApps(currentTimeMillis, deviceUsageTime,
+                    mDatabase.isFirstUpdate());
         }
     }
 
@@ -145,7 +146,8 @@
      * @param firstUpdate if it is the first update, touch all installed apps, otherwise only
      *        touch the system apps
      */
-    private void initializeDefaultsForApps(long currentTimeMillis, boolean firstUpdate) {
+    private void initializeDefaultsForApps(long currentTimeMillis, long deviceUsageTime,
+            boolean firstUpdate) {
         PackageManager pm = mContext.getPackageManager();
         List<PackageInfo> packages = pm.getInstalledPackages(0, mUserId);
         final int packageCount = packages.size();
@@ -153,9 +155,10 @@
             final PackageInfo pi = packages.get(i);
             String packageName = pi.packageName;
             if (pi.applicationInfo != null && (firstUpdate || pi.applicationInfo.isSystemApp())
-                    && getLastPackageAccessTime(packageName) == -1) {
+                    && getBeginIdleTime(packageName) == -1) {
                 for (IntervalStats stats : mCurrentStats) {
                     stats.update(packageName, currentTimeMillis, Event.INTERACTION);
+                    stats.updateBeginIdleTime(packageName, deviceUsageTime);
                     mStatsChanged = true;
                 }
             }
@@ -170,7 +173,7 @@
         loadActiveStats(newTime, true);
     }
 
-    void reportEvent(UsageEvents.Event event) {
+    void reportEvent(UsageEvents.Event event, long deviceUsageTime) {
         if (DEBUG) {
             Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
                     + "[" + event.mTimeStamp + "]: "
@@ -205,6 +208,7 @@
                 stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
             } else {
                 stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+                stats.updateBeginIdleTime(event.mPackage, deviceUsageTime);
             }
         }
 
@@ -215,9 +219,9 @@
      * Sets the last timestamp for each of the intervals.
      * @param lastTimestamp
      */
-    void setLastTimestamp(String packageName, long lastTimestamp) {
+    void setBeginIdleTime(String packageName, long deviceUsageTime) {
         for (IntervalStats stats : mCurrentStats) {
-            stats.update(packageName, lastTimestamp, UsageEvents.Event.NONE);
+            stats.updateBeginIdleTime(packageName, deviceUsageTime);
         }
         notifyStatsChanged();
     }
@@ -376,13 +380,13 @@
         return new UsageEvents(results, table);
     }
 
-    long getLastPackageAccessTime(String packageName) {
+    long getBeginIdleTime(String packageName) {
         final IntervalStats yearly = mCurrentStats[UsageStatsManager.INTERVAL_YEARLY];
         UsageStats packageUsage;
         if ((packageUsage = yearly.packageStats.get(packageName)) == null) {
             return -1;
         } else {
-            return packageUsage.getLastTimeUsed();
+            return packageUsage.getBeginIdleTime();
         }
     }
 
@@ -506,23 +510,23 @@
     // -- DUMP related methods --
     //
 
-    void checkin(final IndentingPrintWriter pw) {
+    void checkin(final IndentingPrintWriter pw, final long screenOnTime) {
         mDatabase.checkinDailyFiles(new UsageStatsDatabase.CheckinAction() {
             @Override
             public boolean checkin(IntervalStats stats) {
-                printIntervalStats(pw, stats, false);
+                printIntervalStats(pw, stats, screenOnTime, false);
                 return true;
             }
         });
     }
 
-    void dump(IndentingPrintWriter pw) {
+    void dump(IndentingPrintWriter pw, final long screenOnTime) {
         // This is not a check-in, only dump in-memory stats.
         for (int interval = 0; interval < mCurrentStats.length; interval++) {
             pw.print("In-memory ");
             pw.print(intervalToString(interval));
             pw.println(" stats");
-            printIntervalStats(pw, mCurrentStats[interval], true);
+            printIntervalStats(pw, mCurrentStats[interval], screenOnTime, true);
         }
     }
 
@@ -540,7 +544,8 @@
         return Long.toString(elapsedTime);
     }
 
-    void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, boolean prettyDates) {
+    void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, long screenOnTime,
+            boolean prettyDates) {
         if (prettyDates) {
             pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext,
                     stats.beginTime, stats.endTime, sDateFormatFlags) + "\"");
@@ -559,6 +564,8 @@
             pw.printPair("package", usageStats.mPackageName);
             pw.printPair("totalTime", formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates));
             pw.printPair("lastTime", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
+            pw.printPair("inactiveTime",
+                    formatElapsedTime(screenOnTime - usageStats.mBeginIdleTime, prettyDates));
             pw.println();
         }
         pw.decreaseIndent();
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 9efea0d..bf1ea4d 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -44,14 +44,12 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.VerificationParams;
 import android.content.pm.VerifierDeviceIdentity;
-import android.content.pm.PackageManager.MoveCallback;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Handler;
-import android.os.ResultReceiver;
 import android.os.UserHandle;
 import android.os.storage.VolumeInfo;
 
@@ -195,13 +193,28 @@
 
     /** @hide */
     @Override
-    public void grantPermission(String packageName, String permissionName, UserHandle user) {
+    public void grantRuntimePermission(String packageName, String permissionName,
+            UserHandle user) {
         throw new UnsupportedOperationException();
     }
 
     /** @hide */
     @Override
-    public void revokePermission(String packageName, String permissionName, UserHandle user) {
+    public void revokeRuntimePermission(String packageName, String permissionName,
+            UserHandle user) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    @Override
+    public int getPermissionFlags(String permissionName, String packageName, UserHandle user) {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    @Override
+    public void updatePermissionFlags(String permissionName, String packageName,
+            int flagMask, int flagValues, UserHandle user) {
         throw new UnsupportedOperationException();
     }