Merge "API Rename: IC#inputContent to IC#commitContent." into nyc-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index 7088764..a69f503 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -420,9 +420,9 @@
     field public static final int contentInsetStart = 16843859; // 0x1010453
     field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522
     field public static final int contextClickable = 16844007; // 0x10104e7
-    field public static final int contextDescription = 16844082; // 0x1010532
+    field public static final int contextDescription = 16844078; // 0x101052e
     field public static final int contextPopupMenuStyle = 16844033; // 0x1010501
-    field public static final int contextUri = 16844081; // 0x1010531
+    field public static final int contextUri = 16844077; // 0x101052d
     field public static final int controlX1 = 16843772; // 0x10103fc
     field public static final int controlX2 = 16843774; // 0x10103fe
     field public static final int controlY1 = 16843773; // 0x10103fd
@@ -1041,7 +1041,7 @@
     field public static final int rotation = 16843558; // 0x1010326
     field public static final int rotationX = 16843559; // 0x1010327
     field public static final int rotationY = 16843560; // 0x1010328
-    field public static final int roundIcon = 16844080; // 0x1010530
+    field public static final int roundIcon = 16844076; // 0x101052c
     field public static final int rowCount = 16843637; // 0x1010375
     field public static final int rowDelay = 16843216; // 0x10101d0
     field public static final int rowEdgeFlags = 16843329; // 0x1010241
@@ -1109,20 +1109,16 @@
     field public static final int shareInterpolator = 16843195; // 0x10101bb
     field public static final int sharedUserId = 16842763; // 0x101000b
     field public static final int sharedUserLabel = 16843361; // 0x1010261
-    field public static final int shortcutCategories = 16844077; // 0x101052d
-    field public static final int shortcutDisabledMessage = 16844076; // 0x101052c
-    field public static final int shortcutIcon = 16844073; // 0x1010529
+    field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
     field public static final int shortcutId = 16844072; // 0x1010528
-    field public static final int shortcutIntentAction = 16844078; // 0x101052e
-    field public static final int shortcutIntentData = 16844079; // 0x101052f
-    field public static final int shortcutLongLabel = 16844075; // 0x101052b
-    field public static final int shortcutShortLabel = 16844074; // 0x101052a
+    field public static final int shortcutLongLabel = 16844074; // 0x101052a
+    field public static final int shortcutShortLabel = 16844073; // 0x1010529
     field public static final int shouldDisableView = 16843246; // 0x10101ee
     field public static final int showAsAction = 16843481; // 0x10102d9
     field public static final int showDefault = 16843258; // 0x10101fa
     field public static final int showDividers = 16843561; // 0x1010329
     field public static final int showForAllUsers = 16844015; // 0x10104ef
-    field public static final int showMetadataInPreview = 16844083; // 0x1010533
+    field public static final int showMetadataInPreview = 16844079; // 0x101052f
     field public static final deprecated int showOnLockScreen = 16843721; // 0x10103c9
     field public static final int showSilent = 16843259; // 0x10101fb
     field public static final int showText = 16843949; // 0x10104ad
@@ -10099,14 +10095,14 @@
     method public android.content.pm.ShortcutInfo build();
     method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
     method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
-    method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
     method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
     method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
     method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
     method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
-    method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
     method public android.content.pm.ShortcutInfo.Builder setRank(int);
-    method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.CharSequence);
   }
 
   public class ShortcutManager {
diff --git a/api/system-current.txt b/api/system-current.txt
index 9b1b765..e30c247 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -527,9 +527,9 @@
     field public static final int contentInsetStart = 16843859; // 0x1010453
     field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522
     field public static final int contextClickable = 16844007; // 0x10104e7
-    field public static final int contextDescription = 16844082; // 0x1010532
+    field public static final int contextDescription = 16844078; // 0x101052e
     field public static final int contextPopupMenuStyle = 16844033; // 0x1010501
-    field public static final int contextUri = 16844081; // 0x1010531
+    field public static final int contextUri = 16844077; // 0x101052d
     field public static final int controlX1 = 16843772; // 0x10103fc
     field public static final int controlX2 = 16843774; // 0x10103fe
     field public static final int controlY1 = 16843773; // 0x10103fd
@@ -1148,7 +1148,7 @@
     field public static final int rotation = 16843558; // 0x1010326
     field public static final int rotationX = 16843559; // 0x1010327
     field public static final int rotationY = 16843560; // 0x1010328
-    field public static final int roundIcon = 16844080; // 0x1010530
+    field public static final int roundIcon = 16844076; // 0x101052c
     field public static final int rowCount = 16843637; // 0x1010375
     field public static final int rowDelay = 16843216; // 0x10101d0
     field public static final int rowEdgeFlags = 16843329; // 0x1010241
@@ -1220,20 +1220,16 @@
     field public static final int shareInterpolator = 16843195; // 0x10101bb
     field public static final int sharedUserId = 16842763; // 0x101000b
     field public static final int sharedUserLabel = 16843361; // 0x1010261
-    field public static final int shortcutCategories = 16844077; // 0x101052d
-    field public static final int shortcutDisabledMessage = 16844076; // 0x101052c
-    field public static final int shortcutIcon = 16844073; // 0x1010529
+    field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
     field public static final int shortcutId = 16844072; // 0x1010528
-    field public static final int shortcutIntentAction = 16844078; // 0x101052e
-    field public static final int shortcutIntentData = 16844079; // 0x101052f
-    field public static final int shortcutLongLabel = 16844075; // 0x101052b
-    field public static final int shortcutShortLabel = 16844074; // 0x101052a
+    field public static final int shortcutLongLabel = 16844074; // 0x101052a
+    field public static final int shortcutShortLabel = 16844073; // 0x1010529
     field public static final int shouldDisableView = 16843246; // 0x10101ee
     field public static final int showAsAction = 16843481; // 0x10102d9
     field public static final int showDefault = 16843258; // 0x10101fa
     field public static final int showDividers = 16843561; // 0x1010329
     field public static final int showForAllUsers = 16844015; // 0x10104ef
-    field public static final int showMetadataInPreview = 16844083; // 0x1010533
+    field public static final int showMetadataInPreview = 16844079; // 0x101052f
     field public static final deprecated int showOnLockScreen = 16843721; // 0x10103c9
     field public static final int showSilent = 16843259; // 0x10101fb
     field public static final int showText = 16843949; // 0x10104ad
@@ -10523,14 +10519,14 @@
     method public android.content.pm.ShortcutInfo build();
     method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
     method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
-    method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
     method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
     method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
     method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
     method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
-    method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
     method public android.content.pm.ShortcutInfo.Builder setRank(int);
-    method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.CharSequence);
   }
 
   public class ShortcutManager {
diff --git a/api/test-current.txt b/api/test-current.txt
index d0d84ec..480d8dc 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -420,9 +420,9 @@
     field public static final int contentInsetStart = 16843859; // 0x1010453
     field public static final int contentInsetStartWithNavigation = 16844066; // 0x1010522
     field public static final int contextClickable = 16844007; // 0x10104e7
-    field public static final int contextDescription = 16844082; // 0x1010532
+    field public static final int contextDescription = 16844078; // 0x101052e
     field public static final int contextPopupMenuStyle = 16844033; // 0x1010501
-    field public static final int contextUri = 16844081; // 0x1010531
+    field public static final int contextUri = 16844077; // 0x101052d
     field public static final int controlX1 = 16843772; // 0x10103fc
     field public static final int controlX2 = 16843774; // 0x10103fe
     field public static final int controlY1 = 16843773; // 0x10103fd
@@ -1041,7 +1041,7 @@
     field public static final int rotation = 16843558; // 0x1010326
     field public static final int rotationX = 16843559; // 0x1010327
     field public static final int rotationY = 16843560; // 0x1010328
-    field public static final int roundIcon = 16844080; // 0x1010530
+    field public static final int roundIcon = 16844076; // 0x101052c
     field public static final int rowCount = 16843637; // 0x1010375
     field public static final int rowDelay = 16843216; // 0x10101d0
     field public static final int rowEdgeFlags = 16843329; // 0x1010241
@@ -1109,20 +1109,16 @@
     field public static final int shareInterpolator = 16843195; // 0x10101bb
     field public static final int sharedUserId = 16842763; // 0x101000b
     field public static final int sharedUserLabel = 16843361; // 0x1010261
-    field public static final int shortcutCategories = 16844077; // 0x101052d
-    field public static final int shortcutDisabledMessage = 16844076; // 0x101052c
-    field public static final int shortcutIcon = 16844073; // 0x1010529
+    field public static final int shortcutDisabledMessage = 16844075; // 0x101052b
     field public static final int shortcutId = 16844072; // 0x1010528
-    field public static final int shortcutIntentAction = 16844078; // 0x101052e
-    field public static final int shortcutIntentData = 16844079; // 0x101052f
-    field public static final int shortcutLongLabel = 16844075; // 0x101052b
-    field public static final int shortcutShortLabel = 16844074; // 0x101052a
+    field public static final int shortcutLongLabel = 16844074; // 0x101052a
+    field public static final int shortcutShortLabel = 16844073; // 0x1010529
     field public static final int shouldDisableView = 16843246; // 0x10101ee
     field public static final int showAsAction = 16843481; // 0x10102d9
     field public static final int showDefault = 16843258; // 0x10101fa
     field public static final int showDividers = 16843561; // 0x1010329
     field public static final int showForAllUsers = 16844015; // 0x10104ef
-    field public static final int showMetadataInPreview = 16844083; // 0x1010533
+    field public static final int showMetadataInPreview = 16844079; // 0x101052f
     field public static final deprecated int showOnLockScreen = 16843721; // 0x10103c9
     field public static final int showSilent = 16843259; // 0x10101fb
     field public static final int showText = 16843949; // 0x10104ad
@@ -10112,14 +10108,14 @@
     method public android.content.pm.ShortcutInfo build();
     method public android.content.pm.ShortcutInfo.Builder setActivity(android.content.ComponentName);
     method public android.content.pm.ShortcutInfo.Builder setCategories(java.util.Set<java.lang.String>);
-    method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder setDisabledMessage(java.lang.CharSequence);
     method public android.content.pm.ShortcutInfo.Builder setExtras(android.os.PersistableBundle);
     method public android.content.pm.ShortcutInfo.Builder setIcon(android.graphics.drawable.Icon);
     method public android.content.pm.ShortcutInfo.Builder setId(java.lang.String);
     method public android.content.pm.ShortcutInfo.Builder setIntent(android.content.Intent);
-    method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder setLongLabel(java.lang.CharSequence);
     method public android.content.pm.ShortcutInfo.Builder setRank(int);
-    method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.String);
+    method public android.content.pm.ShortcutInfo.Builder setShortLabel(java.lang.CharSequence);
   }
 
   public class ShortcutManager {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f12c284..3c7eef5 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1779,9 +1779,10 @@
 
         case START_BACKUP_AGENT_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
-            ApplicationInfo info = ApplicationInfo.CREATOR.createFromParcel(data);
+            String packageName = data.readString();
             int backupRestoreMode = data.readInt();
-            boolean success = bindBackupAgent(info, backupRestoreMode);
+            int userId = data.readInt();
+            boolean success = bindBackupAgent(packageName, backupRestoreMode, userId);
             reply.writeNoException();
             reply.writeInt(success ? 1 : 0);
             return true;
@@ -4448,13 +4449,14 @@
         return binder;
     }
 
-    public boolean bindBackupAgent(ApplicationInfo app, int backupRestoreMode)
+    public boolean bindBackupAgent(String packageName, int backupRestoreMode, int userId)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
-        app.writeToParcel(data, 0);
+        data.writeString(packageName);
         data.writeInt(backupRestoreMode);
+        data.writeInt(userId);
         mRemote.transact(START_BACKUP_AGENT_TRANSACTION, data, reply, 0);
         reply.readException();
         boolean success = reply.readInt() != 0;
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 81788da..ac21346 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -250,7 +250,7 @@
     public IBinder peekService(Intent service, String resolvedType, String callingPackage)
             throws RemoteException;
 
-    public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode)
+    public boolean bindBackupAgent(String packageName, int backupRestoreMode, int userId)
             throws RemoteException;
     public void clearPendingBackup() throws RemoteException;
     public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException;
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 896fa43..064b909 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -30,6 +30,7 @@
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.os.UserHandle;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -245,7 +246,7 @@
         mTextResId = b.mTextResId;
         mDisabledMessage = b.mDisabledMessage;
         mDisabledMessageResId = b.mDisabledMessageResId;
-        mCategories = clone(b.mCategories);
+        mCategories = cloneCategories(b.mCategories);
         mIntent = b.mIntent;
         if (mIntent != null) {
             final Bundle intentExtras = mIntent.getExtras();
@@ -259,8 +260,17 @@
         updateTimestamp();
     }
 
-    private <T> ArraySet<T> clone(Set<T> source) {
-        return (source == null) ? null : new ArraySet<>(source);
+    private ArraySet<String> cloneCategories(Set<String> source) {
+        if (source == null) {
+            return null;
+        }
+        final ArraySet<String> ret = new ArraySet<>(source.size());
+        for (CharSequence s : source) {
+            if (!TextUtils.isEmpty(s)) {
+                ret.add(s.toString().intern());
+            }
+        }
+        return ret;
     }
 
     /**
@@ -304,7 +314,7 @@
             mTextResId = source.mTextResId;
             mDisabledMessage = source.mDisabledMessage;
             mDisabledMessageResId = source.mDisabledMessageResId;
-            mCategories = clone(source.mCategories);
+            mCategories = cloneCategories(source.mCategories);
             if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
                 mIntent = source.mIntent;
                 mIntentPersistableExtras = source.mIntentPersistableExtras;
@@ -614,7 +624,7 @@
             mDisabledMessageResName = null;
         }
         if (source.mCategories != null) {
-            mCategories = clone(source.mCategories);
+            mCategories = cloneCategories(source.mCategories);
         }
         if (source.mIntent != null) {
             mIntent = source.mIntent;
@@ -752,7 +762,7 @@
          * an icon.  The recommend max length is 10 characters.
          */
         @NonNull
-        public Builder setShortLabel(@NonNull String shortLabel) {
+        public Builder setShortLabel(@NonNull CharSequence shortLabel) {
             Preconditions.checkState(mTitleResId == 0, "shortLabelResId already set");
             mTitle = Preconditions.checkStringNotEmpty(shortLabel, "shortLabel");
             return this;
@@ -776,14 +786,14 @@
          * The recommend max length is 25 characters.
          */
         @NonNull
-        public Builder setLongLabel(@NonNull String longLabel) {
+        public Builder setLongLabel(@NonNull CharSequence longLabel) {
             Preconditions.checkState(mTextResId == 0, "longLabelResId already set");
             mText = Preconditions.checkStringNotEmpty(longLabel, "longLabel");
             return this;
         }
 
         /** @hide -- old signature, the internal code still uses it. */
-        public Builder setTitle(@NonNull String value) {
+        public Builder setTitle(@NonNull CharSequence value) {
             return setShortLabel(value);
         }
 
@@ -793,7 +803,7 @@
         }
 
         /** @hide -- old signature, the internal code still uses it. */
-        public Builder setText(@NonNull String value) {
+        public Builder setText(@NonNull CharSequence value) {
             return setLongLabel(value);
         }
 
@@ -813,7 +823,7 @@
         }
 
         @NonNull
-        public Builder setDisabledMessage(@NonNull String disabledMessage) {
+        public Builder setDisabledMessage(@NonNull CharSequence disabledMessage) {
             Preconditions.checkState(
                     mDisabledMessageResId == 0, "disabledMessageResId already set");
             mDisabledMessage =
@@ -1355,6 +1365,37 @@
         mIconResName = iconResName;
     }
 
+    /**
+     * Replaces the intent
+     *
+     * @throws IllegalArgumentException when extra is not compatible with {@link PersistableBundle}.
+     *
+     * @hide
+     */
+    public void setIntent(Intent intent) throws IllegalArgumentException {
+        Preconditions.checkNotNull(intent);
+
+        final Bundle intentExtras = intent.getExtras();
+
+        mIntent = intent;
+
+        if (intentExtras != null) {
+            intent.replaceExtras((Bundle) null);
+            mIntentPersistableExtras = new PersistableBundle(intentExtras);
+        } else {
+            mIntentPersistableExtras = null;
+        }
+    }
+
+    /**
+     * Replaces the categories.
+     *
+     * @hide
+     */
+    public void setCategories(Set<String> categories) {
+        mCategories = cloneCategories(categories);
+    }
+
     private ShortcutInfo(Parcel source) {
         final ClassLoader cl = getClass().getClassLoader();
 
@@ -1591,7 +1632,7 @@
         mDisabledMessage = disabledMessage;
         mDisabledMessageResId = disabledMessageResId;
         mDisabledMessageResName = disabledMessageResName;
-        mCategories = clone(categories);
+        mCategories = cloneCategories(categories);
         mIntent = intent;
         mIntentPersistableExtras = intentPersistableExtras;
         mRank = rank;
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d59c8ac..17834fb 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -464,7 +464,8 @@
             || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {
             getLocationInWindow(mLocation);
 
-            if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
+            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                    + "Changes: creating=" + creating
                     + " format=" + formatChanged + " size=" + sizeChanged
                     + " visible=" + visibleChanged
                     + " left=" + (mWindowSpaceLeft != mLocation[0])
@@ -534,7 +535,8 @@
                     mReportDrawNeeded = false;
                     mDrawingStopped = !visible;
 
-                    if (DEBUG) Log.i(TAG, "Cur surface: " + mSurface);
+                    if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                            + "Cur surface: " + mSurface);
 
                     relayoutResult = mSession.relayout(
                         mWindow, mWindow.mSeq, mLayout, mWindowSpaceWidth, mWindowSpaceHeight,
@@ -547,7 +549,8 @@
                         reportDrawNeeded = true;
                     }
 
-                    if (DEBUG) Log.i(TAG, "New surface: " + mNewSurface
+                    if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                            + "New surface: " + mNewSurface
                             + ", vis=" + visible + ", frame=" + mWinFrame);
 
                     mSurfaceFrame.left = 0;
@@ -581,7 +584,8 @@
                     if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
                         mSurfaceCreated = false;
                         if (mSurface.isValid()) {
-                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed");
+                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                                    + "visibleChanged -- surfaceDestroyed");
                             callbacks = getSurfaceCallbacks();
                             for (SurfaceHolder.Callback c : callbacks) {
                                 c.surfaceDestroyed(mSurfaceHolder);
@@ -594,7 +598,8 @@
                         if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
                             mSurfaceCreated = true;
                             mIsCreating = true;
-                            if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated");
+                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                                    + "visibleChanged -- surfaceCreated");
                             if (callbacks == null) {
                                 callbacks = getSurfaceCallbacks();
                             }
@@ -604,7 +609,8 @@
                         }
                         if (creating || formatChanged || sizeChanged
                                 || visibleChanged || realSizeChanged) {
-                            if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat
+                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                                    + "surfaceChanged -- format=" + mFormat
                                     + " w=" + myWidth + " h=" + myHeight);
                             if (callbacks == null) {
                                 callbacks = getSurfaceCallbacks();
@@ -614,7 +620,8 @@
                             }
                         }
                         if (redrawNeeded) {
-                            if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded");
+                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                                    + "surfaceRedrawNeeded");
                             if (callbacks == null) {
                                 callbacks = getSurfaceCallbacks();
                             }
@@ -629,7 +636,8 @@
                 } finally {
                     mIsCreating = false;
                     if (redrawNeeded) {
-                        if (DEBUG) Log.i(TAG, "finishedDrawing");
+                        if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+                                + "finishedDrawing");
                         mSession.finishDrawing(mWindow);
                     }
                     mSession.performDeferredDestroy(mWindow);
@@ -663,8 +671,9 @@
                 }
 
                 try {
-                    Log.d(TAG, String.format("updateWindowPosition UI, " +
-                            "postion = [%d, %d, %d, %d]", mWinFrame.left, mWinFrame.top,
+                    Log.d(TAG, String.format("%d updateWindowPosition UI, " +
+                            "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
+                            mWinFrame.left, mWinFrame.top,
                             mWinFrame.right, mWinFrame.bottom));
                     mSession.repositionChild(mWindow, mWinFrame.left, mWinFrame.top,
                             mWinFrame.right, mWinFrame.bottom, -1, mWinFrame);
@@ -697,9 +706,9 @@
         }
         try {
             if (DEBUG) {
-                Log.d(TAG, String.format("updateWindowPosition RT, frameNr = %d, " +
-                        "postion = [%d, %d, %d, %d]", frameNumber, left, top,
-                        right, bottom));
+                Log.d(TAG, String.format("%d updateWindowPosition RT, frameNr = %d, " +
+                        "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
+                        frameNumber, left, top, right, bottom));
             }
             // Just using mRTLastReportedPosition as a dummy rect here
             session.repositionChild(window, left, top, right, bottom,
@@ -712,6 +721,25 @@
         }
     }
 
+    /**
+     * Called by native on RenderThread to notify that the window is no longer in the
+     * draw tree
+     * @hide
+     */
+    public final void windowPositionLostRT(long frameNumber) {
+        if (DEBUG) {
+            Log.d(TAG, String.format("%d windowPositionLostRT RT, frameNr = %d",
+                    System.identityHashCode(this), frameNumber));
+        }
+        // TODO: This is a bit of a hack as we don't have an API to report to WM
+        // to hide a window with a frameNumber, so just shift the window very far into
+        // negative space which will do effectively the same thing.
+        // Use the last reported size to avoid influencing the size of the bufferqueue
+        int x = -1000 - mRTLastReportedPosition.width();
+        int y = -1000 - mRTLastReportedPosition.height();
+        updateWindowPositionRT(frameNumber, x, y, -1000, -1000);
+    }
+
     private SurfaceHolder.Callback[] getSurfaceCallbacks() {
         SurfaceHolder.Callback callbacks[];
         synchronized (mCallbacks) {
@@ -750,8 +778,7 @@
                 boolean alwaysConsumeNavBar) {
             SurfaceView surfaceView = mSurfaceView.get();
             if (surfaceView != null) {
-                if (DEBUG) Log.v(
-                        "SurfaceView", surfaceView + " got resized: w=" + frame.width()
+                if (DEBUG) Log.v(TAG, surfaceView + " got resized: w=" + frame.width()
                         + " h=" + frame.height() + ", cur w=" + mCurWidth + " h=" + mCurHeight);
                 surfaceView.mSurfaceLock.lock();
                 try {
@@ -907,7 +934,7 @@
         private final Canvas internalLockCanvas(Rect dirty) {
             mSurfaceLock.lock();
 
-            if (DEBUG) Log.i(TAG, "Locking canvas... stopped="
+            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped="
                     + mDrawingStopped + ", win=" + mWindow);
 
             Canvas c = null;
@@ -919,7 +946,7 @@
                 }
             }
 
-            if (DEBUG) Log.i(TAG, "Returned canvas: " + c);
+            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Returned canvas: " + c);
             if (c != null) {
                 mLastLockTime = SystemClock.uptimeMillis();
                 return c;
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index a6db0f4..4fc546c 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -534,6 +534,7 @@
 // ----------------------------------------------------------------------------
 
 jmethodID gSurfaceViewPositionUpdateMethod;
+jmethodID gSurfaceViewPositionLostMethod;
 
 static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
         jlong renderNodePtr, jobject surfaceview) {
@@ -581,6 +582,20 @@
             info.canvasContext.enqueueFrameWork(std::move(functor));
         }
 
+        virtual void onPositionLost(RenderNode& node, const TreeInfo* info) override {
+            if (CC_UNLIKELY(!mWeakRef || (info && !info->updateWindowPositions))) return;
+
+            if (info) {
+                auto functor = std::bind(
+                    std::mem_fn(&SurfaceViewPositionUpdater::doNotifyPositionLost), this,
+                    (jlong) info->canvasContext.getFrameNumber());
+
+                info->canvasContext.enqueueFrameWork(std::move(functor));
+            } else {
+                doNotifyPositionLost(0);
+            }
+        }
+
     private:
         JNIEnv* jnienv() {
             JNIEnv* env;
@@ -607,6 +622,21 @@
             env->DeleteLocalRef(localref);
         }
 
+        void doNotifyPositionLost(jlong frameNumber) {
+            ATRACE_NAME("SurfaceView position lost");
+
+            JNIEnv* env = jnienv();
+            jobject localref = env->NewLocalRef(mWeakRef);
+            if (CC_UNLIKELY(!localref)) {
+                jnienv()->DeleteWeakGlobalRef(mWeakRef);
+                mWeakRef = nullptr;
+                return;
+            }
+
+            env->CallVoidMethod(localref, gSurfaceViewPositionLostMethod, frameNumber);
+            env->DeleteLocalRef(localref);
+        }
+
         JavaVM* mVm;
         jobject mWeakRef;
     };
@@ -701,6 +731,8 @@
     jclass clazz = FindClassOrDie(env, "android/view/SurfaceView");
     gSurfaceViewPositionUpdateMethod = GetMethodIDOrDie(env, clazz,
             "updateWindowPositionRT", "(JIIII)V");
+    gSurfaceViewPositionLostMethod = GetMethodIDOrDie(env, clazz,
+            "windowPositionLostRT", "(J)V");
     clazz = FindClassOrDie(env, "android/view/RenderNode");
     gOnRenderNodeDetached = GetMethodIDOrDie(env, clazz,
             "onRenderNodeDetached", "()V");
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d0c6a8e..13e1d00 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8251,12 +8251,13 @@
     <declare-styleable name="Shortcut">
         <attr name="shortcutId" format="string" />
         <attr name="enabled" />
-        <attr name="shortcutIcon" format="reference" />
+        <attr name="icon" />
         <attr name="shortcutShortLabel" format="reference" />
         <attr name="shortcutLongLabel" format="reference" />
         <attr name="shortcutDisabledMessage" format="reference" />
-        <attr name="shortcutCategories" format="string" />
-        <attr name="shortcutIntentAction" format="string" />
-        <attr name="shortcutIntentData" format="string" />
+    </declare-styleable>
+
+    <declare-styleable name="ShortcutCategories">
+        <attr name="name" />
     </declare-styleable>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index da31767..c187d2c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2733,13 +2733,9 @@
        =============================================================== -->
     <eat-comment />
     <public type="attr" name="shortcutId" />
-    <public type="attr" name="shortcutIcon" />
     <public type="attr" name="shortcutShortLabel" />
     <public type="attr" name="shortcutLongLabel" />
     <public type="attr" name="shortcutDisabledMessage" />
-    <public type="attr" name="shortcutCategories" />
-    <public type="attr" name="shortcutIntentAction" />
-    <public type="attr" name="shortcutIntentData" />
     <public type="attr" name="roundIcon" />
 
     <public type="attr" name="contextUri" />
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 5a3300a..f8797bf 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -474,7 +474,7 @@
 }
 #endif
 
-void RenderNode::syncDisplayList(TreeObserver* observer) {
+void RenderNode::syncDisplayList(TreeInfo* info) {
     // Make sure we inc first so that we don't fluctuate between 0 and 1,
     // which would thrash the layer cache
     if (mStagingDisplayList) {
@@ -482,7 +482,7 @@
             child->renderNode->incParentRefCount();
         }
     }
-    deleteDisplayList(observer);
+    deleteDisplayList(info ? info->observer : nullptr, info);
     mDisplayList = mStagingDisplayList;
     mStagingDisplayList = nullptr;
     if (mDisplayList) {
@@ -501,15 +501,15 @@
         // Damage with the old display list first then the new one to catch any
         // changes in isRenderable or, in the future, bounds
         damageSelf(info);
-        syncDisplayList(info.observer);
+        syncDisplayList(&info);
         damageSelf(info);
     }
 }
 
-void RenderNode::deleteDisplayList(TreeObserver* observer) {
+void RenderNode::deleteDisplayList(TreeObserver* observer, TreeInfo* info) {
     if (mDisplayList) {
         for (auto&& child : mDisplayList->getChildren()) {
-            child->renderNode->decParentRefCount(observer);
+            child->renderNode->decParentRefCount(observer, info);
         }
     }
     delete mDisplayList;
@@ -541,35 +541,38 @@
     }
 }
 
-void RenderNode::destroyHardwareResources(TreeObserver* observer) {
+void RenderNode::destroyHardwareResources(TreeObserver* observer, TreeInfo* info) {
     if (mLayer) {
         destroyLayer(mLayer);
         mLayer = nullptr;
     }
     if (mDisplayList) {
         for (auto&& child : mDisplayList->getChildren()) {
-            child->renderNode->destroyHardwareResources(observer);
+            child->renderNode->destroyHardwareResources(observer, info);
         }
         if (mNeedsDisplayListSync) {
             // Next prepare tree we are going to push a new display list, so we can
             // drop our current one now
-            deleteDisplayList(observer);
+            deleteDisplayList(observer, info);
         }
     }
 }
 
-void RenderNode::decParentRefCount(TreeObserver* observer) {
+void RenderNode::decParentRefCount(TreeObserver* observer, TreeInfo* info) {
     LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!");
     mParentCount--;
     if (!mParentCount) {
         if (observer) {
             observer->onMaybeRemovedFromTree(this);
         }
+        if (CC_UNLIKELY(mPositionListener.get())) {
+            mPositionListener->onPositionLost(*this, info);
+        }
         // If a child of ours is being attached to our parent then this will incorrectly
         // destroy its hardware resources. However, this situation is highly unlikely
         // and the failure is "just" that the layer is re-created, so this should
         // be safe enough
-        destroyHardwareResources(observer);
+        destroyHardwareResources(observer, info);
     }
 }
 
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index acdc3d8..f80be5e 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -196,7 +196,7 @@
     }
 
     ANDROID_API virtual void prepareTree(TreeInfo& info);
-    void destroyHardwareResources(TreeObserver* observer);
+    void destroyHardwareResources(TreeObserver* observer, TreeInfo* info = nullptr);
 
     // UI thread only!
     ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator);
@@ -228,10 +228,19 @@
     OffscreenBuffer** getLayerHandle() { return &mLayer; } // ugh...
 #endif
 
+    // Note: The position callbacks are relying on the listener using
+    // the frameNumber to appropriately batch/synchronize these transactions.
+    // There is no other filtering/batching to ensure that only the "final"
+    // state called once per frame.
     class ANDROID_API PositionListener {
     public:
         virtual ~PositionListener() {}
+        // Called when the RenderNode's position changes
         virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) = 0;
+        // Called when the RenderNode no longer has a position. As in, it's
+        // no longer being drawn.
+        // Note, tree info might be null
+        virtual void onPositionLost(RenderNode& node, const TreeInfo* info) = 0;
     };
 
     // Note this is not thread safe, this needs to be called
@@ -306,7 +315,7 @@
 
 
     void syncProperties();
-    void syncDisplayList(TreeObserver* observer);
+    void syncDisplayList(TreeInfo* info);
 
     void prepareTreeImpl(TreeInfo& info, bool functorsNeedLayer);
     void pushStagingPropertiesChanges(TreeInfo& info);
@@ -317,11 +326,11 @@
 #endif
     void prepareLayer(TreeInfo& info, uint32_t dirtyMask);
     void pushLayerUpdate(TreeInfo& info);
-    void deleteDisplayList(TreeObserver* observer);
+    void deleteDisplayList(TreeObserver* observer, TreeInfo* info = nullptr);
     void damageSelf(TreeInfo& info);
 
     void incParentRefCount() { mParentCount++; }
-    void decParentRefCount(TreeObserver* observer);
+    void decParentRefCount(TreeObserver* observer, TreeInfo* info = nullptr);
 
     String8 mName;
     sp<VirtualLightRefBase> mUserContext;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 91889d3..542b258 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -134,6 +134,7 @@
             = SystemProperties.getBoolean("debug.child_notifs", true);
     public static final boolean FORCE_REMOTE_INPUT_HISTORY =
             SystemProperties.getBoolean("debug.force_remoteinput_history", false);
+    private static boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
 
     protected static final int MSG_SHOW_RECENT_APPS = 1019;
     protected static final int MSG_HIDE_RECENT_APPS = 1020;
@@ -704,10 +705,13 @@
                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
                 mSettingsObserver,
                 UserHandle.USER_ALL);
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT), false,
-                mSettingsObserver,
-                UserHandle.USER_ALL);
+        if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
+                    false,
+                    mSettingsObserver,
+                    UserHandle.USER_ALL);
+        }
 
         mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
@@ -2304,16 +2308,20 @@
         final boolean allowedByDpm = (dpmFlags
                 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
 
-        final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
-                0,
-                mCurrentUserId) != 0;
-        final boolean remoteInputDpm = (dpmFlags
-                & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
-
-
         setShowLockscreenNotifications(show && allowedByDpm);
-        setLockScreenAllowRemoteInput(remoteInput && remoteInputDpm);
+
+        if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
+            final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                    Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
+                    0,
+                    mCurrentUserId) != 0;
+            final boolean remoteInputDpm =
+                    (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
+
+            setLockScreenAllowRemoteInput(remoteInput && remoteInputDpm);
+        } else {
+            setLockScreenAllowRemoteInput(false);
+        }
     }
 
     protected abstract void setAreThereNotifications();
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 5527a41..4ed0913 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2323,6 +2323,20 @@
     // Deletion helper encountered an error during photo and video deletion.
     ACTION_DELETION_HELPER_PHOTOS_VIDEOS_DELETION_FAIL = 473;
 
+    // OPEN: Settings (root page if there are multiple tabs)
+    // CATEGORY: SETTINGS
+    DASHBOARD_CONTAINER = 474;
+
+    // OPEN: Settings -> SUPPORT TAB
+    // CATEGORY: SETTINGS
+    SUPPORT_FRAGMENT = 475;
+
+    // ACTION: Settings -> Select summary tab.
+    ACTION_SELECT_SUMMARY=476;
+
+    // ACTION: Settings -> Select support tab.
+    ACTION_SELECT_SUPPORT_FRAGMENT = 477;
+
     // ---- End N-MR1 Constants, all N-MR1 constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 04a0990..feceb14 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -20,6 +20,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
+import android.annotation.UserIdInt;
 import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -67,6 +68,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.StorageManager;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.AtomicFile;
@@ -650,7 +652,7 @@
     }
 
     private void ensureGroupStateLoadedLocked(int userId, boolean enforceUserUnlockingOrUnlocked) {
-        if (enforceUserUnlockingOrUnlocked && !mUserManager.isUserUnlockingOrUnlocked(userId)) {
+        if (enforceUserUnlockingOrUnlocked && !isUserRunningAndUnlocked(userId)) {
             throw new IllegalStateException(
                     "User " + userId + " must be unlocked for widgets to be available");
         }
@@ -695,6 +697,10 @@
         loadGroupStateLocked(newProfileIds);
     }
 
+    private boolean isUserRunningAndUnlocked(@UserIdInt int userId) {
+        return mUserManager.isUserRunning(userId) && StorageManager.isUserKeyUnlocked(userId);
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
@@ -3381,7 +3387,7 @@
             if (userInfo != null && userInfo.isManagedProfile()) {
                 UserInfo parentInfo = mUserManager.getProfileParent(userId);
                 if (parentInfo != null
-                        && !mUserManager.isUserUnlockingOrUnlocked(parentInfo.getUserHandle())) {
+                        && !isUserRunningAndUnlocked(parentInfo.getUserHandle().getIdentifier())) {
                     return true;
                 }
             }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 334b228..930c151b 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -2356,7 +2356,8 @@
             mConnecting = true;
             mConnectedAgent = null;
             try {
-                if (mActivityManager.bindBackupAgent(app, mode)) {
+                if (mActivityManager.bindBackupAgent(app.packageName, mode,
+                        UserHandle.USER_OWNER)) {
                     Slog.d(TAG, "awaiting agent for " + app);
 
                     // success; wait for the agent to arrive
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c044181..bfc9877 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17041,11 +17041,22 @@
     // Cause the target app to be launched if necessary and its backup agent
     // instantiated.  The backup agent will invoke backupAgentCreated() on the
     // activity manager to announce its creation.
-    public boolean bindBackupAgent(ApplicationInfo app, int backupMode) {
-        if (DEBUG_BACKUP) Slog.v(TAG_BACKUP,
-                "bindBackupAgent: app=" + app + " mode=" + backupMode);
+    public boolean bindBackupAgent(String packageName, int backupMode, int userId) {
+        if (DEBUG_BACKUP) Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode);
         enforceCallingPermission("android.permission.CONFIRM_FULL_BACKUP", "bindBackupAgent");
 
+        IPackageManager pm = AppGlobals.getPackageManager();
+        ApplicationInfo app = null;
+        try {
+            app = pm.getApplicationInfo(packageName, 0, userId);
+        } catch (RemoteException e) {
+            // can't happen; package manager is process-local
+        }
+        if (app == null) {
+            Slog.w(TAG, "Unable to bind backup agent for " + packageName);
+            return false;
+        }
+
         synchronized(this) {
             // !!! TODO: currently no check here that we're already bound
             BatteryStatsImpl.Uid.Pkg.Serv ss = null;
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index ace14ac..d637586 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -682,6 +682,8 @@
             changed |= pushOutExcessShortcuts();
         }
 
+        s.verifyStates();
+
         if (changed) {
             // This will send a notification to the launcher, and also save .
             s.packageShortcutsChanged(getPackageName(), getPackageUserId());
@@ -774,6 +776,7 @@
             }
             removeOrphans();
         }
+        adjustRanks();
         return changed;
     }
 
@@ -810,7 +813,6 @@
                 deleteDynamicWithId(shortcut.getId());
             }
         }
-        service.verifyStates();
 
         return changed;
     }
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index ec19927..c349b75 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -24,10 +24,10 @@
 import android.content.pm.ShortcutInfo;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.net.Uri;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.Slog;
 import android.util.Xml;
 
@@ -48,10 +48,12 @@
     private static final boolean DEBUG = ShortcutService.DEBUG || false; // DO NOT SUBMIT WITH TRUE
 
     @VisibleForTesting
-    static final String METADATA_KEY = "android.pm.Shortcuts";
+    static final String METADATA_KEY = "android.app.shortcuts";
 
     private static final String TAG_SHORTCUTS = "shortcuts";
     private static final String TAG_SHORTCUT = "shortcut";
+    private static final String TAG_INTENT = "intent";
+    private static final String TAG_CATEGORIES = "categories";
 
     @Nullable
     public static List<ShortcutInfo> parseShortcuts(ShortcutService service,
@@ -60,10 +62,17 @@
 
         List<ShortcutInfo> result = null;
 
-        if (pi != null && pi.activities != null) {
-            for (ActivityInfo activityInfo : pi.activities) {
-                result = parseShortcutsOneFile(service, activityInfo, packageName, userId, result);
+        try {
+            if (pi != null && pi.activities != null) {
+                for (ActivityInfo activityInfo : pi.activities) {
+                    result = parseShortcutsOneFile(service, activityInfo, packageName, userId, result);
+                }
             }
+        } catch (RuntimeException e) {
+            // Resource ID mismatch may cause various runtime exceptions when parsing XMLs.
+            service.wtf(
+                    "Exception caught while parsing shortcut XML for package=" + packageName, e);
+            return null;
         }
         return result;
     }
@@ -89,14 +98,58 @@
             final int maxShortcuts = service.getMaxActivityShortcuts();
             int numShortcuts = 0;
 
+            // We instantiate ShortcutInfo at <shortcut>, but we add it to the list at </shortcut>,
+            // after parsing <intent>.  We keep the current one in here.
+            ShortcutInfo currentShortcut = null;
+
+            Set<String> categories = null;
+
             outer:
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                     && (type != XmlPullParser.END_TAG || parser.getDepth() > 0)) {
+                final int depth = parser.getDepth();
+                final String tag = parser.getName();
+
+                // When a shortcut tag is closing, publish.
+                if ((type == XmlPullParser.END_TAG) && (depth == 2) && (TAG_SHORTCUT.equals(tag))) {
+                    if (currentShortcut == null) {
+                        // Shortcut was invalid.
+                        continue;
+                    }
+                    final ShortcutInfo si = currentShortcut;
+                    currentShortcut = null; // Make sure to null out for the next iteration.
+
+                    if (si.getIntent() == null) {
+                        Log.e(TAG, "Shortcut " + si.getId() + " has no intent. Skipping it.");
+                        continue;
+                    }
+
+                    if (numShortcuts >= maxShortcuts) {
+                        Log.e(TAG, "More than " + maxShortcuts + " shortcuts found for "
+                                + activityInfo.getComponentName() + ". Skipping the rest.");
+                        return result;
+                    }
+                    if (categories != null) {
+                        si.setCategories(categories);
+                        categories = null;
+                    }
+
+                    if (result == null) {
+                        result = new ArrayList<>();
+                    }
+                    result.add(si);
+                    numShortcuts++;
+                    rank++;
+                    if (ShortcutService.DEBUG) {
+                        Slog.d(TAG, "Shortcut added: " + si.toInsecureString());
+                    }
+                    continue;
+                }
+
+                // Otherwise, just look at start tags.
                 if (type != XmlPullParser.START_TAG) {
                     continue;
                 }
-                final int depth = parser.getDepth();
-                final String tag = parser.getName();
 
                 if (depth == 1 && TAG_SHORTCUTS.equals(tag)) {
                     continue; // Root tag.
@@ -104,35 +157,73 @@
                 if (depth == 2 && TAG_SHORTCUT.equals(tag)) {
                     final ShortcutInfo si = parseShortcutAttributes(
                             service, attrs, packageName, activity, userId, rank);
+                    if (si == null) {
+                        // Shortcut was invalid.
+                        continue;
+                    }
                     if (ShortcutService.DEBUG) {
-                        Slog.d(TAG, "Shortcut=" + si);
+                        Slog.d(TAG, "Shortcut found: " + si.toInsecureString());
                     }
                     if (result != null) {
                         for (int i = result.size() - 1; i >= 0; i--) {
                             if (si.getId().equals(result.get(i).getId())) {
-                                Slog.w(TAG, "Duplicate shortcut ID detected, skipping.");
+                                Log.e(TAG, "Duplicate shortcut ID detected. Skipping it.");
                                 continue outer;
                             }
                         }
                     }
+                    if (!si.isEnabled()) {
+                        // Just set the default intent to disabled shortcuts.
+                        si.setIntent(new Intent(Intent.ACTION_VIEW));
+                    }
+                    currentShortcut = si;
+                    categories = null;
+                    continue;
+                }
+                if (depth == 3 && TAG_INTENT.equals(tag)) {
+                    if ((currentShortcut == null)
+                            || (currentShortcut.getIntentNoExtras() != null)
+                            || !currentShortcut.isEnabled()) {
+                        Log.e(TAG, "Ignoring excessive intent tag.");
+                        continue;
+                    }
 
-                    if (si != null) {
-                        if (numShortcuts >= maxShortcuts) {
-                            Slog.w(TAG, "More than " + maxShortcuts + " shortcuts found for "
-                                    + activityInfo.getComponentName() + ", ignoring the rest.");
-                            return result;
-                        }
-
-                        if (result == null) {
-                            result = new ArrayList<>();
-                        }
-                        result.add(si);
-                        numShortcuts++;
-                        rank++;
+                    final Intent intent = Intent.parseIntent(service.mContext.getResources(),
+                            parser, attrs);
+                    if (TextUtils.isEmpty(intent.getAction())) {
+                        Log.e(TAG, "Shortcut intent action must be provided. activity=" + activity);
+                        continue;
+                    }
+                    try {
+                        currentShortcut.setIntent(intent);
+                    } catch (RuntimeException e) {
+                        // This shouldn't happen because intents in XML can't have complicated
+                        // extras, but just in case Intent.parseIntent() supports such a thing one
+                        // day.
+                        Log.e(TAG, "Shortcut's extras contain un-persistable values. Skipping it.");
+                        continue;
                     }
                     continue;
                 }
-                Slog.w(TAG, "Unknown tag " + tag);
+                if (depth == 3 && TAG_CATEGORIES.equals(tag)) {
+                    if ((currentShortcut == null)
+                            || (currentShortcut.getCategories() != null)) {
+                        continue;
+                    }
+                    final String name = parseCategories(service, attrs);
+                    if (TextUtils.isEmpty(name)) {
+                        Log.e(TAG, "Empty category found. activity=" + activity);
+                        continue;
+                    }
+
+                    if (categories == null) {
+                        categories = new ArraySet<>();
+                    }
+                    categories.add(name);
+                    continue;
+                }
+
+                Log.w(TAG, "Unknown tag " + tag + " at depth " + depth);
             }
         } finally {
             if (parser != null) {
@@ -142,6 +233,16 @@
         return result;
     }
 
+    private static String parseCategories(ShortcutService service, AttributeSet attrs) {
+        final TypedArray sa = service.mContext.getResources().obtainAttributes(attrs,
+                R.styleable.ShortcutCategories);
+        try {
+            return sa.getString(R.styleable.ShortcutCategories_name);
+        } finally {
+            sa.recycle();
+        }
+    }
+
     private static ShortcutInfo parseShortcutAttributes(ShortcutService service,
             AttributeSet attrs, String packageName, ComponentName activity,
             @UserIdInt int userId, int rank) {
@@ -150,14 +251,11 @@
         try {
             final String id = sa.getString(R.styleable.Shortcut_shortcutId);
             final boolean enabled = sa.getBoolean(R.styleable.Shortcut_enabled, true);
-            final int iconResId = sa.getResourceId(R.styleable.Shortcut_shortcutIcon, 0);
+            final int iconResId = sa.getResourceId(R.styleable.Shortcut_icon, 0);
             final int titleResId = sa.getResourceId(R.styleable.Shortcut_shortcutShortLabel, 0);
             final int textResId = sa.getResourceId(R.styleable.Shortcut_shortcutLongLabel, 0);
             final int disabledMessageResId = sa.getResourceId(
                     R.styleable.Shortcut_shortcutDisabledMessage, 0);
-            final String categories = sa.getString(R.styleable.Shortcut_shortcutCategories);
-            String intentAction = sa.getString(R.styleable.Shortcut_shortcutIntentAction);
-            final String intentData = sa.getString(R.styleable.Shortcut_shortcutIntentData);
 
             if (TextUtils.isEmpty(id)) {
                 Slog.w(TAG, "Shortcut ID must be provided. activity=" + activity);
@@ -167,31 +265,6 @@
                 Slog.w(TAG, "Shortcut title must be provided. activity=" + activity);
                 return null;
             }
-            if (TextUtils.isEmpty(intentAction)) {
-                if (enabled) {
-                    Slog.w(TAG, "Shortcut intent action must be provided. activity=" + activity);
-                    return null;
-                } else {
-                    // Disabled shortcut doesn't have to have an action, but just set VIEW as the
-                    // default.
-                    intentAction = Intent.ACTION_VIEW;
-                }
-            }
-
-            final ArraySet<String> categoriesSet;
-            if (categories == null) {
-                categoriesSet = null;
-            } else {
-                final String[] arr = categories.split(":");
-                categoriesSet = new ArraySet<>(arr.length);
-                for (String v : arr) {
-                    categoriesSet.add(v);
-                }
-            }
-            final Intent intent = new Intent(intentAction);
-            if (!TextUtils.isEmpty(intentData)) {
-                intent.setData(Uri.parse(intentData));
-            }
 
             return createShortcutFromManifest(
                     service,
@@ -202,8 +275,6 @@
                     titleResId,
                     textResId,
                     disabledMessageResId,
-                    categoriesSet,
-                    intent,
                     rank,
                     iconResId,
                     enabled);
@@ -214,8 +285,8 @@
 
     private static ShortcutInfo createShortcutFromManifest(ShortcutService service,
             @UserIdInt int userId, String id, String packageName, ComponentName activityComponent,
-            int titleResId, int textResId, int disabledMessageResId, Set<String> categories,
-            Intent intent, int rank, int iconResId, boolean enabled) {
+            int titleResId, int textResId, int disabledMessageResId,
+            int rank, int iconResId, boolean enabled) {
 
         final int flags =
                 (enabled ? ShortcutInfo.FLAG_MANIFEST : ShortcutInfo.FLAG_DISABLED)
@@ -239,8 +310,8 @@
                 null, // disabled message string
                 disabledMessageResId,
                 null, // disabled message res name
-                categories,
-                intent,
+                null, // categories
+                null, // intent
                 null, // intent extras
                 rank,
                 null, // extras
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 1619168..be8eeed 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1510,6 +1510,10 @@
             ps.clearAllImplicitRanks();
             assignImplicitRanks(newShortcuts);
 
+            for (int i = 0; i < size; i++) {
+                fixUpIncomingShortcutInfo(newShortcuts.get(i), /* forUpdate= */ false);
+            }
+
             // First, remove all un-pinned; dynamic shortcuts
             ps.deleteAllDynamicShortcuts();
 
diff --git a/services/tests/servicestests/res/drawable/icon3.png b/services/tests/servicestests/res/drawable/icon3.png
new file mode 100644
index 0000000..64eb294
--- /dev/null
+++ b/services/tests/servicestests/res/drawable/icon3.png
Binary files differ
diff --git a/services/tests/servicestests/res/xml/shortcut_1.xml b/services/tests/servicestests/res/xml/shortcut_1.xml
index f6d54fc..e3f9172 100644
--- a/services/tests/servicestests/res/xml/shortcut_1.xml
+++ b/services/tests/servicestests/res/xml/shortcut_1.xml
@@ -2,12 +2,17 @@
     <shortcut
         android:shortcutId="ms1"
         android:enabled="true"
-        android:shortcutIcon="@drawable/icon1"
+        android:icon="@drawable/icon1"
         android:shortcutShortLabel="@string/shortcut_title1"
         android:shortcutLongLabel="@string/shortcut_text1"
         android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
-        android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
-        android:shortcutIntentAction="action1"
-        android:shortcutIntentData="data1"
-    />
+        >
+        <intent
+            android:action="action1"
+            android:data="data1"
+            >
+        </intent>
+        <categories android:name="android.shortcut.conversation" />
+        <categories android:name="android.shortcut.media" />
+    </shortcut>
 </shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_1_alt.xml b/services/tests/servicestests/res/xml/shortcut_1_alt.xml
index bf14f49..2d5e8e7 100644
--- a/services/tests/servicestests/res/xml/shortcut_1_alt.xml
+++ b/services/tests/servicestests/res/xml/shortcut_1_alt.xml
@@ -17,12 +17,17 @@
     <shortcut
         android:shortcutId="ms1-alt"
         android:enabled="true"
-        android:shortcutIcon="@drawable/icon1"
+        android:icon="@drawable/icon1"
         android:shortcutShortLabel="@string/shortcut_title1"
         android:shortcutLongLabel="@string/shortcut_text1"
         android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
-        android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
-        android:shortcutIntentAction="action1"
-        android:shortcutIntentData="data1"
-    />
+        >
+        <intent
+            android:action="action1"
+            android:data="data1"
+        >
+        </intent>
+        <categories android:name="android.shortcut.conversation" />
+        <categories android:name="android.shortcut.media" />
+    </shortcut>
 </shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_1_disable.xml b/services/tests/servicestests/res/xml/shortcut_1_disable.xml
index 81a84b4..e3ee3a0 100644
--- a/services/tests/servicestests/res/xml/shortcut_1_disable.xml
+++ b/services/tests/servicestests/res/xml/shortcut_1_disable.xml
@@ -17,7 +17,7 @@
     <shortcut
         android:shortcutId="ms1"
         android:enabled="false"
-        android:shortcutIcon="@drawable/icon2"
+        android:icon="@drawable/icon2"
         android:shortcutShortLabel="@string/shortcut_title2"
         android:shortcutLongLabel="@string/shortcut_text2"
         android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
diff --git a/services/tests/servicestests/res/xml/shortcut_2.xml b/services/tests/servicestests/res/xml/shortcut_2.xml
index 96ed382..f7ea803 100644
--- a/services/tests/servicestests/res/xml/shortcut_2.xml
+++ b/services/tests/servicestests/res/xml/shortcut_2.xml
@@ -17,23 +17,32 @@
     <shortcut
         android:shortcutId="ms1"
         android:enabled="true"
-        android:shortcutIcon="@drawable/icon1"
+        android:icon="@drawable/icon1"
         android:shortcutShortLabel="@string/shortcut_title1"
         android:shortcutLongLabel="@string/shortcut_text1"
         android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
-        android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
-        android:shortcutIntentAction="action1"
-        android:shortcutIntentData="http://a.b.c/"
-    />
+        >
+        <intent
+            android:action="action1"
+            android:data="http://a.b.c/"
+            >
+        </intent>
+        <categories android:name="android.shortcut.conversation" />
+        <categories android:name="android.shortcut.media" />
+    </shortcut>
     <shortcut
         android:shortcutId="ms2"
         android:enabled="true"
-        android:shortcutIcon="@drawable/icon2"
+        android:icon="@drawable/icon2"
         android:shortcutShortLabel="@string/shortcut_title2"
         android:shortcutLongLabel="@string/shortcut_text2"
         android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
-        android:shortcutCategories="android.shortcut.conversation"
-        android:shortcutIntentAction="action2"
-        android:shortcutIntentData="http://a.b.c/2"
-    />
+        >
+        <intent
+            android:action="action2"
+            android:data="http://a.b.c/2"
+            >
+        </intent>
+        <categories android:name="android.shortcut.conversation" />
+    </shortcut>
 </shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_2_duplicate.xml b/services/tests/servicestests/res/xml/shortcut_2_duplicate.xml
index 2f814b7..b00ec60 100644
--- a/services/tests/servicestests/res/xml/shortcut_2_duplicate.xml
+++ b/services/tests/servicestests/res/xml/shortcut_2_duplicate.xml
@@ -17,11 +17,19 @@
     <shortcut
         android:shortcutId="ms1"
         android:shortcutShortLabel="@string/shortcut_title1"
-        android:shortcutIntentAction="action1"
-    />
+        >
+        <intent
+            android:action="action1"
+            >
+        </intent>
+    </shortcut>
     <shortcut
         android:shortcutId="ms1"
         android:shortcutShortLabel="@string/shortcut_title2"
-        android:shortcutIntentAction="action2"
-    />
+        >
+        <intent
+            android:action="action2"
+            >
+        </intent>
+    </shortcut>
 </shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_5.xml b/services/tests/servicestests/res/xml/shortcut_5.xml
index 56dba0e..9551100 100644
--- a/services/tests/servicestests/res/xml/shortcut_5.xml
+++ b/services/tests/servicestests/res/xml/shortcut_5.xml
@@ -17,37 +17,69 @@
     <shortcut
         android:shortcutId="ms1"
         android:enabled="true"
-        android:shortcutIcon="@drawable/icon1"
+        android:icon="@drawable/icon1"
         android:shortcutShortLabel="@string/shortcut_title1"
         android:shortcutLongLabel="@string/shortcut_text1"
         android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
-        android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
-        android:shortcutIntentAction="action1"
-        android:shortcutIntentData="http://a.b.c/1"
-    />
+        >
+        <intent
+            android:action="action1"
+            android:data="http://a.b.c/1"
+            >
+        </intent>
+        <categories android:name="android.shortcut.conversation" />
+        <categories android:name="android.shortcut.media" />
+    </shortcut>
     <shortcut
         android:shortcutId="ms2"
         android:enabled="true"
-        android:shortcutIcon="@drawable/icon2"
+        android:icon="@drawable/icon2"
         android:shortcutShortLabel="@string/shortcut_title2"
         android:shortcutLongLabel="@string/shortcut_text2"
         android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
-        android:shortcutCategories="android.shortcut.conversation"
-        android:shortcutIntentAction="action2"
-    />
+        >
+        <intent
+            android:action="action2"
+            >
+        </intent>
+        <categories android:name="android.shortcut.conversation" />
+    </shortcut>
     <shortcut
         android:shortcutId="ms3"
         android:shortcutShortLabel="@string/shortcut_title1"
-        android:shortcutIntentAction="android.intent.action.VIEW"
-    />
+        >
+        <intent
+            android:action="android.intent.action.VIEW"
+            >
+        </intent>
+    </shortcut>
     <shortcut
         android:shortcutId="ms4"
-        android:shortcutShortLabel="@string/shortcut_title1"
-        android:shortcutIntentAction="android.intent.action.VIEW"
-    />
+        android:shortcutShortLabel="@string/shortcut_title2"
+        >
+        <intent
+            android:action="android.intent.action.VIEW2"
+            >
+        </intent>
+        <categories />
+        <categories android:name="" />
+        <categories android:name="cat" />
+    </shortcut>
     <shortcut
         android:shortcutId="ms5"
         android:shortcutShortLabel="@string/shortcut_title1"
-        android:shortcutIntentAction="android.intent.action.VIEW"
-    />
+        >
+        <intent
+            android:action="action"
+            android:data="http://www/"
+            android:targetPackage="abc"
+            android:targetClass=".xyz"
+            android:mimeType="foo/bar"
+            >
+            <categories android:name="cat1" />
+            <categories android:name="cat2" />
+            <extra android:name="key1" android:value="value1" />
+            <extra android:name="key2" android:value="value2" />
+        </intent>
+    </shortcut>
 </shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_5_alt.xml b/services/tests/servicestests/res/xml/shortcut_5_alt.xml
index 74085d9..f79cd6f 100644
--- a/services/tests/servicestests/res/xml/shortcut_5_alt.xml
+++ b/services/tests/servicestests/res/xml/shortcut_5_alt.xml
@@ -17,37 +17,58 @@
     <shortcut
         android:shortcutId="ms1_alt"
         android:enabled="true"
-        android:shortcutIcon="@drawable/icon1"
+        android:icon="@drawable/icon1"
         android:shortcutShortLabel="@string/shortcut_title1"
         android:shortcutLongLabel="@string/shortcut_text1"
         android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
-        android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
-        android:shortcutIntentAction="action1"
-        android:shortcutIntentData="http://a.b.c/1"
-    />
+        >
+        <intent
+            android:action="action1"
+            android:data="http://a.b.c/1"
+            >
+        </intent>
+        <categories android:name="android.shortcut.conversation" />
+        <categories android:name="android.shortcut.media" />
+    </shortcut>
     <shortcut
         android:shortcutId="ms2_alt"
         android:enabled="true"
-        android:shortcutIcon="@drawable/icon2"
+        android:icon="@drawable/icon2"
         android:shortcutShortLabel="@string/shortcut_title2"
         android:shortcutLongLabel="@string/shortcut_text2"
         android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
-        android:shortcutCategories="android.shortcut.conversation"
-        android:shortcutIntentAction="action2"
-    />
+        >
+        <intent
+            android:action="action2"
+            >
+        </intent>
+        <categories android:name="android.shortcut.conversation" />
+    </shortcut>
     <shortcut
         android:shortcutId="ms3_alt"
         android:shortcutShortLabel="@string/shortcut_title1"
-        android:shortcutIntentAction="android.intent.action.VIEW"
-    />
+        >
+        <intent
+            android:action="android.intent.action.VIEW"
+            >
+        </intent>
+    </shortcut>
     <shortcut
         android:shortcutId="ms4_alt"
         android:shortcutShortLabel="@string/shortcut_title1"
-        android:shortcutIntentAction="android.intent.action.VIEW"
-    />
+        >
+        <intent
+            android:action="android.intent.action.VIEW"
+            >
+        </intent>
+    </shortcut>
     <shortcut
         android:shortcutId="ms5_alt"
         android:shortcutShortLabel="@string/shortcut_title1"
-        android:shortcutIntentAction="android.intent.action.VIEW"
-    />
+        >
+        <intent
+            android:action="android.intent.action.VIEW"
+            >
+        </intent>
+    </shortcut>
 </shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_5_reverse.xml b/services/tests/servicestests/res/xml/shortcut_5_reverse.xml
index 3d6eb22..d5b7c8f 100644
--- a/services/tests/servicestests/res/xml/shortcut_5_reverse.xml
+++ b/services/tests/servicestests/res/xml/shortcut_5_reverse.xml
@@ -17,37 +17,62 @@
     <shortcut
         android:shortcutId="ms5"
         android:enabled="true"
-        android:shortcutIcon="@drawable/icon1"
+        android:icon="@drawable/icon1"
         android:shortcutShortLabel="@string/shortcut_title1"
         android:shortcutLongLabel="@string/shortcut_text1"
         android:shortcutDisabledMessage="@string/shortcut_disabled_message1"
-        android:shortcutCategories="android.shortcut.conversation:android.shortcut.media"
-        android:shortcutIntentAction="action1"
-        android:shortcutIntentData="http://a.b.c/1"
-    />
+        >
+        <intent
+            android:action="action1"
+            android:data="http://a.b.c/1"
+            >
+        </intent>
+        <categories android:name="android.shortcut.conversation" />
+        <categories android:name="android.shortcut.media" />
+    </shortcut>
     <shortcut
         android:shortcutId="ms4"
         android:enabled="true"
-        android:shortcutIcon="@drawable/icon2"
+        android:icon="@drawable/icon2"
         android:shortcutShortLabel="@string/shortcut_title2"
         android:shortcutLongLabel="@string/shortcut_text2"
         android:shortcutDisabledMessage="@string/shortcut_disabled_message2"
-        android:shortcutCategories="android.shortcut.conversation"
-        android:shortcutIntentAction="action2"
-    />
+        >
+        <intent
+            android:action="action2"
+            >
+        </intent>
+        <categories android:name="android.shortcut.conversation" />
+    </shortcut>
     <shortcut
         android:shortcutId="ms3"
         android:shortcutShortLabel="@string/shortcut_title1"
-        android:shortcutIntentAction="android.intent.action.VIEW"
-    />
+        >
+        <intent
+            android:action="android.intent.action.VIEW"
+            >
+        </intent>
+    </shortcut>
     <shortcut
         android:shortcutId="ms2"
         android:shortcutShortLabel="@string/shortcut_title1"
-        android:shortcutIntentAction="android.intent.action.VIEW"
-    />
+        >
+        <intent
+            android:action="android.intent.action.VIEW"
+            >
+        </intent>
+    </shortcut>
     <shortcut
         android:shortcutId="ms1"
         android:shortcutShortLabel="@string/shortcut_title1"
-        android:shortcutIntentAction="android.intent.action.VIEW"
-    />
+        >
+        <intent
+            android:action="action"
+            >
+            <categories android:name="cat1" />
+            <categories android:name="cat2" />
+            <extra android:name="key1" android:value="value1" />
+            <extra android:name="key2" android:value="value2" />
+        </intent>
+    </shortcut>
 </shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_error_1.xml b/services/tests/servicestests/res/xml/shortcut_error_1.xml
index 5822496..3990d02 100644
--- a/services/tests/servicestests/res/xml/shortcut_error_1.xml
+++ b/services/tests/servicestests/res/xml/shortcut_error_1.xml
@@ -16,11 +16,19 @@
 <shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
     <shortcut
         android:shortcutShortLabel="@string/shortcut_title1"
-        android:shortcutIntentAction="android.intent.action.VIEW"
-    />
+        >
+        <intent
+            android:action="android.intent.action.VIEW"
+            >
+        </intent>
+    </shortcut>
     <shortcut
         android:shortcutId="x1"
         android:shortcutShortLabel="@string/shortcut_title1"
-        android:shortcutIntentAction="android.intent.action.VIEW"
-    />
+        >
+        <intent
+            android:action="android.intent.action.VIEW"
+            >
+        </intent>
+    </shortcut>
 </shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_error_2.xml b/services/tests/servicestests/res/xml/shortcut_error_2.xml
index ca67ec7..a6f7150 100644
--- a/services/tests/servicestests/res/xml/shortcut_error_2.xml
+++ b/services/tests/servicestests/res/xml/shortcut_error_2.xml
@@ -16,11 +16,19 @@
 <shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
     <shortcut
         android:shortcutId="manifest-shortcut-3"
-        android:shortcutIntentAction="android.intent.action.VIEW"
-    />
+        >
+        <intent
+            android:action="android.intent.action.VIEW"
+            >
+        </intent>
+    </shortcut>
     <shortcut
         android:shortcutId="x2"
         android:shortcutShortLabel="@string/shortcut_title1"
-        android:shortcutIntentAction="android.intent.action.VIEW"
-    />
+        >
+        <intent
+            android:action="android.intent.action.VIEW"
+            >
+        </intent>
+    </shortcut>
 </shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_error_3.xml b/services/tests/servicestests/res/xml/shortcut_error_3.xml
index fb7b31c..a7b9b84 100644
--- a/services/tests/servicestests/res/xml/shortcut_error_3.xml
+++ b/services/tests/servicestests/res/xml/shortcut_error_3.xml
@@ -21,6 +21,10 @@
     <shortcut
         android:shortcutId="x3"
         android:shortcutShortLabel="@string/shortcut_title1"
-        android:shortcutIntentAction="android.intent.action.VIEW"
-    />
+        >
+        <intent
+            android:action="android.intent.action.VIEW"
+            >
+        </intent>
+    </shortcut>
 </shortcuts>
diff --git a/services/tests/servicestests/res/xml/shortcut_error_4.xml b/services/tests/servicestests/res/xml/shortcut_error_4.xml
new file mode 100644
index 0000000..3697bb4
--- /dev/null
+++ b/services/tests/servicestests/res/xml/shortcut_error_4.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+<shortcuts xmlns:android="http://schemas.android.com/apk/res/android" >
+    <!-- This is valid -->
+    <shortcut
+        android:shortcutId="ms1"
+        android:shortcutShortLabel="@string/shortcut_title1"
+        >
+        <intent android:action="action1" />
+    </shortcut>
+
+    <!-- Invalid: no intent -->
+    <shortcut
+        android:shortcutId="ms_ignored1"
+        android:shortcutShortLabel="@string/shortcut_title1"
+        />
+
+    <!-- Valid: more than one intent; first one will be picked. -->
+    <shortcut
+        android:shortcutId="ms2"
+        android:shortcutShortLabel="@string/shortcut_title1"
+        >
+        <intent android:action="action2_1" />
+        <intent android:action="action2_2" />
+    </shortcut>
+
+    <!-- Valid: disabled shortcut doesn't need an intent -->
+    <shortcut
+        android:shortcutId="ms3"
+        android:enabled="false"
+        android:shortcutShortLabel="@string/shortcut_title1"
+        />
+
+    <!-- Valid, but disabled shortcut's intent will be ignored. -->
+    <shortcut
+        android:shortcutId="ms4"
+        android:enabled="false"
+        android:shortcutShortLabel="@string/shortcut_title1"
+        >
+        <intent android:action="action4" />
+    </shortcut>
+
+    <!-- Invalid, no intent action -->
+    <shortcut
+        android:shortcutId="ms_ignored2"
+        android:shortcutShortLabel="@string/shortcut_title1"
+        >
+        <intent android:data="x"/>
+    </shortcut>
+</shortcuts>
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index de06047..56232c0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -4884,6 +4884,117 @@
         });
     }
 
+    public void testManifestShortcuts_intentDefinitions() {
+        addManifestShortcutResource(
+                new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+                R.xml.shortcut_error_4);
+        updatePackageVersion(CALLING_PACKAGE_1, 1);
+        mService.mPackageMonitor.onReceive(getTestContext(),
+                genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            // Make sure invalid ones are not published.
+            // Note that at this point disabled ones don't show up because they weren't pinned.
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1", "ms2")
+                    .areAllManifest()
+                    .areAllNotDynamic()
+                    .areAllNotPinned()
+                    .areAllImmutable()
+                    .areAllEnabled()
+                    .forShortcutWithId("ms1", si -> {
+                        assertTrue(si.isEnabled());
+                        assertEquals("action1", si.getIntent().getAction());
+                    })
+                    .forShortcutWithId("ms2", si -> {
+                        assertTrue(si.isEnabled());
+                        assertEquals("action2_1", si.getIntent().getAction());
+                    });
+        });
+
+        // Publish 5 enabled to pin some, so we can later test disabled manfiest shortcuts..
+        addManifestShortcutResource(
+                new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+                R.xml.shortcut_5);
+        updatePackageVersion(CALLING_PACKAGE_1, 1);
+        mService.mPackageMonitor.onReceive(getTestContext(),
+                genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            // Make sure 5 manifest shortcuts are published.
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1", "ms2", "ms3", "ms4", "ms5")
+                    .areAllManifest()
+                    .areAllNotDynamic()
+                    .areAllNotPinned()
+                    .areAllImmutable()
+                    .areAllEnabled();
+        });
+
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+                    list("ms3", "ms4", "ms5"), HANDLE_USER_0);
+        });
+
+        // Make sure they're pinned.
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1", "ms2", "ms3", "ms4", "ms5")
+                    .selectByIds("ms1", "ms2")
+                    .areAllNotPinned()
+                    .areAllEnabled()
+
+                    .revertToOriginalList()
+                    .selectByIds("ms3", "ms4", "ms5")
+                    .areAllPinned()
+                    .areAllEnabled();
+        });
+
+        // Update the app.
+        addManifestShortcutResource(
+                new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+                R.xml.shortcut_error_4);
+        updatePackageVersion(CALLING_PACKAGE_1, 1);
+        mService.mPackageMonitor.onReceive(getTestContext(),
+                genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+        // Make sure 3, 4 and 5 still exist but disabled.
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1", "ms2", "ms3", "ms4", "ms5")
+                    .areAllNotDynamic()
+                    .areAllImmutable()
+
+                    .selectByIds("ms1", "ms2")
+                    .areAllManifest()
+                    .areAllNotPinned()
+                    .areAllEnabled()
+
+                    .revertToOriginalList()
+                    .selectByIds("ms3", "ms4", "ms5")
+                    .areAllNotManifest()
+                    .areAllPinned()
+                    .areAllDisabled()
+
+                    .revertToOriginalList()
+                    .forShortcutWithId("ms1", si -> {
+                        assertEquals(si.getId(), "action1", si.getIntent().getAction());
+                    })
+                    .forShortcutWithId("ms2", si -> {
+                        assertEquals(si.getId(), "action2_1", si.getIntent().getAction());
+                    })
+                    .forShortcutWithId("ms3", si -> {
+                        assertEquals(si.getId(), Intent.ACTION_VIEW, si.getIntent().getAction());
+                    })
+                    .forShortcutWithId("ms4", si -> {
+                        assertEquals(si.getId(), Intent.ACTION_VIEW, si.getIntent().getAction());
+                    })
+                    .forShortcutWithId("ms5", si -> {
+                        assertEquals(si.getId(), "action", si.getIntent().getAction());
+                    });
+        });
+    }
+
     public void testManifestShortcuts_checkAllFields() {
         mService.handleUnlockUser(USER_0);
 
@@ -4897,63 +5008,95 @@
 
         // Only the valid one is published.
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
-            assertShortcutIds(assertAllManifest(assertAllImmutable(assertAllEnabled(
-                    mManager.getManifestShortcuts()))),
-                    "ms1", "ms2", "ms3", "ms4", "ms5");
+            assertWith(getCallerShortcuts())
+                    .haveIds("ms1", "ms2", "ms3", "ms4", "ms5")
+                    .areAllManifest()
+                    .areAllImmutable()
+                    .areAllEnabled()
+                    .areAllNotPinned()
+                    .areAllNotDynamic()
 
-            // check first shortcut.
-            ShortcutInfo si = getCallerShortcut("ms1");
+                    .forShortcutWithId("ms1", si -> {
+                        assertEquals(R.drawable.icon1, si.getIconResourceId());
+                        assertEquals(new ComponentName(CALLING_PACKAGE_1,
+                                ShortcutActivity.class.getName()),
+                                si.getActivity());
 
-            assertEquals("ms1", si.getId());
-            assertEquals(R.drawable.icon1, si.getIconResourceId());
-            assertEquals(new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
-                    si.getActivity());
+                        assertEquals(R.string.shortcut_title1, si.getTitleResId());
+                        assertEquals("r" + R.string.shortcut_title1, si.getTitleResName());
+                        assertEquals(R.string.shortcut_text1, si.getTextResId());
+                        assertEquals("r" + R.string.shortcut_text1, si.getTextResName());
+                        assertEquals(R.string.shortcut_disabled_message1,
+                                si.getDisabledMessageResourceId());
+                        assertEquals("r" + R.string.shortcut_disabled_message1,
+                                si.getDisabledMessageResName());
 
-            assertEquals(R.string.shortcut_title1, si.getTitleResId());
-            assertEquals("r" + R.string.shortcut_title1, si.getTitleResName());
-            assertEquals(R.string.shortcut_text1, si.getTextResId());
-            assertEquals("r" + R.string.shortcut_text1, si.getTextResName());
-            assertEquals(R.string.shortcut_disabled_message1, si.getDisabledMessageResourceId());
-            assertEquals("r" + R.string.shortcut_disabled_message1, si.getDisabledMessageResName());
+                        assertEquals(set("android.shortcut.conversation", "android.shortcut.media"),
+                                si.getCategories());
+                        assertEquals("action1", si.getIntent().getAction());
+                        assertEquals(Uri.parse("http://a.b.c/1"), si.getIntent().getData());
+                    })
 
-            assertEquals(set("android.shortcut.conversation", "android.shortcut.media"),
-                    si.getCategories());
-            assertEquals("action1", si.getIntent().getAction());
-            assertEquals(Uri.parse("http://a.b.c/1"), si.getIntent().getData());
+                    .forShortcutWithId("ms2", si -> {
+                        assertEquals("ms2", si.getId());
+                        assertEquals(R.drawable.icon2, si.getIconResourceId());
 
-            // check another
-            si = getCallerShortcut("ms2");
+                        assertEquals(R.string.shortcut_title2, si.getTitleResId());
+                        assertEquals("r" + R.string.shortcut_title2, si.getTitleResName());
+                        assertEquals(R.string.shortcut_text2, si.getTextResId());
+                        assertEquals("r" + R.string.shortcut_text2, si.getTextResName());
+                        assertEquals(R.string.shortcut_disabled_message2,
+                                si.getDisabledMessageResourceId());
+                        assertEquals("r" + R.string.shortcut_disabled_message2,
+                                si.getDisabledMessageResName());
 
-            assertEquals("ms2", si.getId());
-            assertEquals(R.drawable.icon2, si.getIconResourceId());
+                        assertEquals(set("android.shortcut.conversation"), si.getCategories());
+                        assertEquals("action2", si.getIntent().getAction());
+                        assertEquals(null, si.getIntent().getData());
+                    })
 
-            assertEquals(R.string.shortcut_title2, si.getTitleResId());
-            assertEquals("r" + R.string.shortcut_title2, si.getTitleResName());
-            assertEquals(R.string.shortcut_text2, si.getTextResId());
-            assertEquals("r" + R.string.shortcut_text2, si.getTextResName());
-            assertEquals(R.string.shortcut_disabled_message2, si.getDisabledMessageResourceId());
-            assertEquals("r" + R.string.shortcut_disabled_message2, si.getDisabledMessageResName());
+                    .forShortcutWithId("ms3", si -> {
+                        assertEquals(0, si.getIconResourceId());
+                        assertEquals(R.string.shortcut_title1, si.getTitleResId());
+                        assertEquals("r" + R.string.shortcut_title1, si.getTitleResName());
 
-            assertEquals(set("android.shortcut.conversation"), si.getCategories());
-            assertEquals("action2", si.getIntent().getAction());
-            assertEquals(null, si.getIntent().getData());
+                        assertEquals(0, si.getTextResId());
+                        assertEquals(null, si.getTextResName());
+                        assertEquals(0, si.getDisabledMessageResourceId());
+                        assertEquals(null, si.getDisabledMessageResName());
 
-            // check another
-            si = getCallerShortcut("ms3");
+                        assertEmpty(si.getCategories());
+                        assertEquals("android.intent.action.VIEW", si.getIntent().getAction());
+                        assertEquals(null, si.getIntent().getData());
+                    })
 
-            assertEquals("ms3", si.getId());
-            assertEquals(0, si.getIconResourceId());
-            assertEquals(R.string.shortcut_title1, si.getTitleResId());
-            assertEquals("r" + R.string.shortcut_title1, si.getTitleResName());
+                    .forShortcutWithId("ms4", si -> {
+                        assertEquals(0, si.getIconResourceId());
+                        assertEquals(R.string.shortcut_title2, si.getTitleResId());
+                        assertEquals("r" + R.string.shortcut_title2, si.getTitleResName());
 
-            assertEquals(0, si.getTextResId());
-            assertEquals(null, si.getTextResName());
-            assertEquals(0, si.getDisabledMessageResourceId());
-            assertEquals(null, si.getDisabledMessageResName());
+                        assertEquals(0, si.getTextResId());
+                        assertEquals(null, si.getTextResName());
+                        assertEquals(0, si.getDisabledMessageResourceId());
+                        assertEquals(null, si.getDisabledMessageResName());
 
-            assertEquals(null, si.getCategories());
-            assertEquals("android.intent.action.VIEW", si.getIntent().getAction());
-            assertEquals(null, si.getIntent().getData());
+                        assertEquals(set("cat"), si.getCategories());
+                        assertEquals("android.intent.action.VIEW2", si.getIntent().getAction());
+                        assertEquals(null, si.getIntent().getData());
+                    })
+
+                    .forShortcutWithId("ms5", si -> {
+                        si = getCallerShortcut("ms5");
+                        assertEquals("action", si.getIntent().getAction());
+                        assertEquals("http://www/", si.getIntent().getData().toString());
+                        assertEquals("foo/bar", si.getIntent().getType());
+                        assertEquals(
+                                new ComponentName("abc", ".xyz"), si.getIntent().getComponent());
+
+                        assertEquals(set("cat1", "cat2"), si.getIntent().getCategories());
+                        assertEquals("value1", si.getIntent().getStringExtra("key1"));
+                        assertEquals("value2", si.getIntent().getStringExtra("key2"));
+                    });
         });
     }
 
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 7ba4c68..712bc1e 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -43,9 +43,11 @@
 import android.os.ParcelFileDescriptor;
 import android.os.UserHandle;
 import android.test.MoreAsserts;
+import android.util.ArraySet;
 import android.util.Log;
 
 import junit.framework.Assert;
+import junit.framework.AssertionFailedError;
 
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
@@ -66,6 +68,7 @@
 import java.util.SortedSet;
 import java.util.TreeSet;
 import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
@@ -259,9 +262,12 @@
         }
     }
 
-    public static <T> List<T> assertEmpty(List<T> list) {
-        assertEquals(0, list.size());
-        return list;
+    public static <T extends Collection<?>> T assertEmpty(T collection) {
+        if (collection == null) {
+            return collection; // okay.
+        }
+        assertEquals(0, collection.size());
+        return collection;
     }
 
     public static List<ShortcutInfo> filter(List<ShortcutInfo> list, Predicate<ShortcutInfo> p) {
@@ -653,42 +659,73 @@
      * New style assertion that allows chained calls.
      */
     public static class ShortcutListAsserter {
+        private final ShortcutListAsserter mOriginal;
         private final List<ShortcutInfo> mList;
 
         ShortcutListAsserter(List<ShortcutInfo> list) {
+            this(null, list);
+        }
+
+        private ShortcutListAsserter(ShortcutListAsserter original, List<ShortcutInfo> list) {
+            mOriginal = original == null ? this : original;
             mList = new ArrayList<>(list);
         }
 
+        public ShortcutListAsserter revertToOriginalList() {
+            return mOriginal;
+        }
+
         public ShortcutListAsserter selectDynamic() {
-            return new ShortcutListAsserter(
+            return new ShortcutListAsserter(this,
                     filter(mList, ShortcutInfo::isDynamic));
         }
 
         public ShortcutListAsserter selectManifest() {
-            return new ShortcutListAsserter(
+            return new ShortcutListAsserter(this,
                     filter(mList, ShortcutInfo::isManifestShortcut));
         }
 
         public ShortcutListAsserter selectPinned() {
-            return new ShortcutListAsserter(
+            return new ShortcutListAsserter(this,
                     filter(mList, ShortcutInfo::isPinned));
         }
 
         public ShortcutListAsserter selectByActivity(ComponentName activity) {
-            return new ShortcutListAsserter(
+            return new ShortcutListAsserter(this,
                     ShortcutManagerTestUtils.filterByActivity(mList, activity));
         }
 
         public ShortcutListAsserter selectByChangedSince(long time) {
-            return new ShortcutListAsserter(
+            return new ShortcutListAsserter(this,
                     ShortcutManagerTestUtils.changedSince(mList, time));
         }
 
+        public ShortcutListAsserter selectByIds(String... ids) {
+            final Set<String> idSet = set(ids);
+            final ArrayList<ShortcutInfo> selected = new ArrayList<>();
+            for (ShortcutInfo si : mList) {
+                if (idSet.contains(si.getId())) {
+                    selected.add(si);
+                    idSet.remove(si.getId());
+                }
+            }
+            if (idSet.size() > 0) {
+                fail("Shortcuts not found for IDs=" + idSet);
+            }
+
+            return new ShortcutListAsserter(this, selected);
+        }
+
         public ShortcutListAsserter toSortByRank() {
-            return new ShortcutListAsserter(
+            return new ShortcutListAsserter(this,
                     ShortcutManagerTestUtils.sortedByRank(mList));
         }
 
+        public ShortcutListAsserter call(Consumer<List<ShortcutInfo>> c) {
+            c.accept(mList);
+            return this;
+        }
+
         public ShortcutListAsserter haveIds(String... expectedIds) {
             assertShortcutIds(mList, expectedIds);
             return this;
@@ -701,7 +738,8 @@
 
         private ShortcutListAsserter haveSequentialRanks() {
             for (int i = 0; i < mList.size(); i++) {
-                assertEquals("Rank not sequential", i, mList.get(i).getRank());
+                final ShortcutInfo si = mList.get(i);
+                assertEquals("Rank not sequential: id=" + si.getId(), i, si.getRank());
             }
             return this;
         }
@@ -717,5 +755,87 @@
             assertEquals(0, mList.size());
             return this;
         }
+
+        public ShortcutListAsserter areAllDynamic() {
+            forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isDynamic()));
+            return this;
+        }
+
+        public ShortcutListAsserter areAllNotDynamic() {
+            forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isDynamic()));
+            return this;
+        }
+
+        public ShortcutListAsserter areAllPinned() {
+            forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isPinned()));
+            return this;
+        }
+
+        public ShortcutListAsserter areAllNotPinned() {
+            forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isPinned()));
+            return this;
+        }
+
+        public ShortcutListAsserter areAllManifest() {
+            forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isManifestShortcut()));
+            return this;
+        }
+
+        public ShortcutListAsserter areAllNotManifest() {
+            forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isManifestShortcut()));
+            return this;
+        }
+
+        public ShortcutListAsserter areAllImmutable() {
+            forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isImmutable()));
+            return this;
+        }
+
+        public ShortcutListAsserter areAllMutable() {
+            forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isImmutable()));
+            return this;
+        }
+
+        public ShortcutListAsserter areAllEnabled() {
+            forAllShortcuts(s -> assertTrue("id=" + s.getId(), s.isEnabled()));
+            return this;
+        }
+
+        public ShortcutListAsserter areAllDisabled() {
+            forAllShortcuts(s -> assertFalse("id=" + s.getId(), s.isEnabled()));
+            return this;
+        }
+
+        public ShortcutListAsserter forAllShortcuts(Consumer<ShortcutInfo> sa) {
+            for (int i = 0; i < mList.size(); i++) {
+                final ShortcutInfo si = mList.get(i);
+                sa.accept(si);
+            }
+            return this;
+        }
+
+        public ShortcutListAsserter forShortcut(Predicate<ShortcutInfo> p,
+                Consumer<ShortcutInfo> sa) {
+            boolean found = false;
+            for (int i = 0; i < mList.size(); i++) {
+                final ShortcutInfo si = mList.get(i);
+                if (p.test(si)) {
+                    found = true;
+                    try {
+                        sa.accept(si);
+                    } catch (Throwable e) {
+                        throw new AssertionError("Assertion failed for shortcut " + si.getId(), e);
+                    }
+                }
+            }
+            assertTrue("Shortcut with the given condition not found.", found);
+            return this;
+        }
+
+        public ShortcutListAsserter forShortcutWithId(String id, Consumer<ShortcutInfo> sa) {
+            forShortcut(si -> si.getId().equals(id), sa);
+
+            return this;
+        }
     }
 }
diff --git a/telephony/java/android/telephony/TelephonyHistogram.java b/telephony/java/android/telephony/TelephonyHistogram.java
index 9d9ce72..e1c3d7b 100644
--- a/telephony/java/android/telephony/TelephonyHistogram.java
+++ b/telephony/java/android/telephony/TelephonyHistogram.java
@@ -31,35 +31,35 @@
 public final class TelephonyHistogram implements Parcelable {
     // Type of Telephony histogram Eg: RIL histogram will have all timing data associated with
     // RIL calls. Similarly we can have any other Telephony histogram.
-    private final int category;
+    private final int mCategory;
 
     // Unique Id identifying a sample within particular category of histogram
-    private final int id;
+    private final int mId;
 
     // Min time taken in ms
-    private int minTimeMs;
+    private int mMinTimeMs;
 
     // Max time taken in ms
-    private int maxTimeMs;
+    private int mMaxTimeMs;
 
     // Average time taken in ms
-    private int averageTimeMs;
+    private int mAverageTimeMs;
 
     // Total count of samples
-    private int sampleCount;
+    private int mSampleCount;
 
     // Array storing time taken for first #RANGE_CALCULATION_COUNT samples of histogram.
-    private int[] initialTimings;
+    private int[] mInitialTimings;
 
     // Total number of time ranges expected (must be greater than 1)
-    private final int bucketCount;
+    private final int mBucketCount;
 
     // Array storing endpoints of range buckets. Calculated based on values of minTime & maxTime
     // after totalTimeCount is #RANGE_CALCULATION_COUNT.
-    private final int[] bucketEndPoints;
+    private final int[] mBucketEndPoints;
 
     // Array storing counts for each time range starting from smallest value range
-    private final int[] bucketCounters;
+    private final int[] mBucketCounters;
 
     /**
      * Constant for Telephony category
@@ -81,69 +81,85 @@
         if (bucketCount <= 1) {
             throw new IllegalArgumentException("Invalid number of buckets");
         }
-        this.category = category;
-        this.id = id;
-        this.minTimeMs = Integer.MAX_VALUE;
-        this.maxTimeMs = 0;
-        this.averageTimeMs = 0;
-        this.sampleCount = 0;
-        initialTimings = new int[RANGE_CALCULATION_COUNT];
-        this.bucketCount = bucketCount;
-        bucketEndPoints = new int[bucketCount - 1];
-        bucketCounters = new int[bucketCount];
+        mCategory = category;
+        mId = id;
+        mMinTimeMs = Integer.MAX_VALUE;
+        mMaxTimeMs = 0;
+        mAverageTimeMs = 0;
+        mSampleCount = 0;
+        mInitialTimings = new int[RANGE_CALCULATION_COUNT];
+        mBucketCount = bucketCount;
+        mBucketEndPoints = new int[bucketCount - 1];
+        mBucketCounters = new int[bucketCount];
     }
 
     public TelephonyHistogram(TelephonyHistogram th) {
-        category = th.getCategory();
-        id = th.getId();
-        minTimeMs = th.getMinTime();
-        maxTimeMs = th.getMaxTime();
-        averageTimeMs = th.getAverageTime();
-        sampleCount = th.getSampleCount();
-        initialTimings = th.getInitialTimings();
-        bucketCount = th.getBucketCount();
-        bucketEndPoints = th.getBucketEndPoints();
-        bucketCounters = th.getBucketCounters();
+        mCategory = th.getCategory();
+        mId = th.getId();
+        mMinTimeMs = th.getMinTime();
+        mMaxTimeMs = th.getMaxTime();
+        mAverageTimeMs = th.getAverageTime();
+        mSampleCount = th.getSampleCount();
+        mInitialTimings = th.getInitialTimings();
+        mBucketCount = th.getBucketCount();
+        mBucketEndPoints = th.getBucketEndPoints();
+        mBucketCounters = th.getBucketCounters();
     }
 
     public int getCategory() {
-        return category;
+        return mCategory;
     }
 
     public int getId() {
-        return id;
+        return mId;
     }
 
     public int getMinTime() {
-        return minTimeMs;
+        return mMinTimeMs;
     }
 
     public int getMaxTime() {
-        return maxTimeMs;
+        return mMaxTimeMs;
     }
 
     public int getAverageTime() {
-        return averageTimeMs;
+        return mAverageTimeMs;
     }
 
     public int getSampleCount () {
-        return sampleCount;
+        return mSampleCount;
     }
 
     private int[] getInitialTimings() {
-        return initialTimings;
+        return mInitialTimings;
     }
 
     public int getBucketCount() {
-        return bucketCount;
+        return mBucketCount;
     }
 
     public int[] getBucketEndPoints() {
-        return getDeepCopyOfArray(bucketEndPoints);
+        if (mSampleCount > 1 && mSampleCount < 10) {
+            int[] tempEndPoints = new int[mBucketCount - 1];
+            calculateBucketEndPoints(tempEndPoints);
+            return tempEndPoints;
+        } else {
+            return getDeepCopyOfArray(mBucketEndPoints);
+        }
     }
 
     public int[] getBucketCounters() {
-        return getDeepCopyOfArray(bucketCounters);
+        if (mSampleCount > 1 && mSampleCount < 10) {
+            int[] tempEndPoints = new int[mBucketCount - 1];
+            int[] tempBucketCounters = new int[mBucketCount];
+            calculateBucketEndPoints(tempEndPoints);
+            for (int j = 0; j < mSampleCount; j++) {
+                addToBucketCounter(tempEndPoints, tempBucketCounters, mInitialTimings[j]);
+            }
+            return tempBucketCounters;
+        } else {
+            return getDeepCopyOfArray(mBucketCounters);
+        }
     }
 
     private int[] getDeepCopyOfArray(int[] array) {
@@ -152,7 +168,7 @@
         return clone;
     }
 
-    private void addToBucketCounter(int time) {
+    private void addToBucketCounter(int[] bucketEndPoints, int[] bucketCounters, int time) {
         int i;
         for (i = 0; i < bucketEndPoints.length; i++) {
             if (time <= bucketEndPoints[i]) {
@@ -163,6 +179,13 @@
         bucketCounters[i]++;
     }
 
+    private void calculateBucketEndPoints(int[] bucketEndPoints) {
+        for (int i = 1; i < mBucketCount; i++) {
+            int endPt = mMinTimeMs + (i * (mMaxTimeMs - mMinTimeMs)) / mBucketCount;
+            bucketEndPoints[i - 1] = endPt;
+        }
+    }
+
     // Add new value of time taken
     // This function updates minTime, maxTime, averageTime & totalTimeCount every time it is
     // called. initialTimings[] is updated if totalTimeCount <= #RANGE_CALCULATION_COUNT. When
@@ -172,65 +195,62 @@
     public void addTimeTaken(int time) {
         // Initialize all fields if its first entry or if integer overflow is going to occur while
         // trying to calculate averageTime
-        if (sampleCount == 0 || (sampleCount == Integer.MAX_VALUE)) {
-            if (sampleCount == 0) {
-                minTimeMs = time;
-                maxTimeMs = time;
-                averageTimeMs = time;
+        if (mSampleCount == 0 || (mSampleCount == Integer.MAX_VALUE)) {
+            if (mSampleCount == 0) {
+                mMinTimeMs = time;
+                mMaxTimeMs = time;
+                mAverageTimeMs = time;
             } else {
-                initialTimings = new int[RANGE_CALCULATION_COUNT];
+                mInitialTimings = new int[RANGE_CALCULATION_COUNT];
             }
-            sampleCount = 1;
-            Arrays.fill(initialTimings, 0);
-            initialTimings[0] = time;
-            Arrays.fill(bucketEndPoints, 0);
-            Arrays.fill(bucketCounters, 0);
+            mSampleCount = 1;
+            Arrays.fill(mInitialTimings, 0);
+            mInitialTimings[0] = time;
+            Arrays.fill(mBucketEndPoints, 0);
+            Arrays.fill(mBucketCounters, 0);
         } else {
-            if (time < minTimeMs) {
-                minTimeMs = time;
+            if (time < mMinTimeMs) {
+                mMinTimeMs = time;
             }
-            if (time > maxTimeMs) {
-                maxTimeMs = time;
+            if (time > mMaxTimeMs) {
+                mMaxTimeMs = time;
             }
-            long totalTime = ((long)averageTimeMs) * sampleCount + time;
-            averageTimeMs = (int)(totalTime/++sampleCount);
+            long totalTime = ((long)mAverageTimeMs) * mSampleCount + time;
+            mAverageTimeMs = (int)(totalTime/++mSampleCount);
 
-            if (sampleCount < RANGE_CALCULATION_COUNT) {
-                initialTimings[sampleCount - 1] = time;
-            } else if (sampleCount == RANGE_CALCULATION_COUNT) {
-                initialTimings[sampleCount - 1] = time;
+            if (mSampleCount < RANGE_CALCULATION_COUNT) {
+                mInitialTimings[mSampleCount - 1] = time;
+            } else if (mSampleCount == RANGE_CALCULATION_COUNT) {
+                mInitialTimings[mSampleCount - 1] = time;
 
                 // Calculate bucket endpoints based on bucketCount expected
-                for (int i = 1; i < bucketCount; i++) {
-                    int endPt = minTimeMs + (i * (maxTimeMs - minTimeMs)) / bucketCount;
-                    bucketEndPoints[i - 1] = endPt;
-                }
+                calculateBucketEndPoints(mBucketEndPoints);
 
                 // Use values stored in initialTimings[] to update bucketCounters
                 for (int j = 0; j < RANGE_CALCULATION_COUNT; j++) {
-                    addToBucketCounter(initialTimings[j]);
+                    addToBucketCounter(mBucketEndPoints, mBucketCounters, mInitialTimings[j]);
                 }
-                initialTimings = null;
+                mInitialTimings = null;
             } else {
-                addToBucketCounter(time);
+                addToBucketCounter(mBucketEndPoints, mBucketCounters, time);
             }
 
         }
     }
 
     public String toString() {
-        String basic = " Histogram id = " + id + " Time(ms): min = " + minTimeMs + " max = "
-                + maxTimeMs + " avg = " + averageTimeMs + " Count = " + sampleCount;
-        if (sampleCount < RANGE_CALCULATION_COUNT) {
+        String basic = " Histogram id = " + mId + " Time(ms): min = " + mMinTimeMs + " max = "
+                + mMaxTimeMs + " avg = " + mAverageTimeMs + " Count = " + mSampleCount;
+        if (mSampleCount < RANGE_CALCULATION_COUNT) {
             return basic;
         } else {
             StringBuffer intervals = new StringBuffer(" Interval Endpoints:");
-            for (int i = 0; i < bucketEndPoints.length; i++) {
-                intervals.append(" " + bucketEndPoints[i]);
+            for (int i = 0; i < mBucketEndPoints.length; i++) {
+                intervals.append(" " + mBucketEndPoints[i]);
             }
             intervals.append(" Interval counters:");
-            for (int i = 0; i < bucketCounters.length; i++) {
-                intervals.append(" " + bucketCounters[i]);
+            for (int i = 0; i < mBucketCounters.length; i++) {
+                intervals.append(" " + mBucketCounters[i]);
             }
             return basic + intervals;
         }
@@ -251,39 +271,39 @@
             };
 
     public TelephonyHistogram(Parcel in) {
-        category = in.readInt();
-        id = in.readInt();
-        minTimeMs = in.readInt();
-        maxTimeMs = in.readInt();
-        averageTimeMs = in.readInt();
-        sampleCount = in.readInt();
+        mCategory = in.readInt();
+        mId = in.readInt();
+        mMinTimeMs = in.readInt();
+        mMaxTimeMs = in.readInt();
+        mAverageTimeMs = in.readInt();
+        mSampleCount = in.readInt();
         if (in.readInt() == PRESENT) {
-            initialTimings = new int[RANGE_CALCULATION_COUNT];
-            in.readIntArray(initialTimings);
+            mInitialTimings = new int[RANGE_CALCULATION_COUNT];
+            in.readIntArray(mInitialTimings);
         }
-        bucketCount = in.readInt();
-        bucketEndPoints = new int[bucketCount - 1];
-        in.readIntArray(bucketEndPoints);
-        bucketCounters = new int[bucketCount];
-        in.readIntArray(bucketCounters);
+        mBucketCount = in.readInt();
+        mBucketEndPoints = new int[mBucketCount - 1];
+        in.readIntArray(mBucketEndPoints);
+        mBucketCounters = new int[mBucketCount];
+        in.readIntArray(mBucketCounters);
     }
 
     public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(category);
-        out.writeInt(id);
-        out.writeInt(minTimeMs);
-        out.writeInt(maxTimeMs);
-        out.writeInt(averageTimeMs);
-        out.writeInt(sampleCount);
-        if (initialTimings == null) {
+        out.writeInt(mCategory);
+        out.writeInt(mId);
+        out.writeInt(mMinTimeMs);
+        out.writeInt(mMaxTimeMs);
+        out.writeInt(mAverageTimeMs);
+        out.writeInt(mSampleCount);
+        if (mInitialTimings == null) {
             out.writeInt(ABSENT);
         } else {
             out.writeInt(PRESENT);
-            out.writeIntArray(initialTimings);
+            out.writeIntArray(mInitialTimings);
         }
-        out.writeInt(bucketCount);
-        out.writeIntArray(bucketEndPoints);
-        out.writeIntArray(bucketCounters);
+        out.writeInt(mBucketCount);
+        out.writeIntArray(mBucketEndPoints);
+        out.writeIntArray(mBucketCounters);
     }
 
     @Override