Merge "Change the color of loading spinner to the color of selected roots."
diff --git a/api/current.txt b/api/current.txt
index 3419fba..1f4bdda 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -20141,6 +20141,7 @@
     field public static final deprecated java.lang.String TAG_APERTURE = "FNumber";
     field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
     field public static final java.lang.String TAG_ARTIST = "Artist";
+    field public static final java.lang.String TAG_ASPECT_FRAME = "AspectFrame";
     field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
     field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
     field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
@@ -20227,6 +20228,8 @@
     field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
     field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
     field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+    field public static final java.lang.String TAG_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
+    field public static final java.lang.String TAG_PREVIEW_IMAGE_START = "PreviewImageStart";
     field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
     field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
     field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
@@ -20254,6 +20257,7 @@
     field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
     field public static final deprecated java.lang.String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
     field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+    field public static final java.lang.String TAG_THUMBNAIL_IMAGE = "ThumbnailImage";
     field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
     field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
     field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
diff --git a/api/system-current.txt b/api/system-current.txt
index 7ea8fdd..228f585 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -21651,6 +21651,7 @@
     field public static final deprecated java.lang.String TAG_APERTURE = "FNumber";
     field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
     field public static final java.lang.String TAG_ARTIST = "Artist";
+    field public static final java.lang.String TAG_ASPECT_FRAME = "AspectFrame";
     field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
     field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
     field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
@@ -21737,6 +21738,8 @@
     field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
     field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
     field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+    field public static final java.lang.String TAG_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
+    field public static final java.lang.String TAG_PREVIEW_IMAGE_START = "PreviewImageStart";
     field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
     field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
     field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
@@ -21764,6 +21767,7 @@
     field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
     field public static final deprecated java.lang.String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
     field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+    field public static final java.lang.String TAG_THUMBNAIL_IMAGE = "ThumbnailImage";
     field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
     field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
     field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
diff --git a/api/test-current.txt b/api/test-current.txt
index 0ccc347..33291dc 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -20211,6 +20211,7 @@
     field public static final deprecated java.lang.String TAG_APERTURE = "FNumber";
     field public static final java.lang.String TAG_APERTURE_VALUE = "ApertureValue";
     field public static final java.lang.String TAG_ARTIST = "Artist";
+    field public static final java.lang.String TAG_ASPECT_FRAME = "AspectFrame";
     field public static final java.lang.String TAG_BITS_PER_SAMPLE = "BitsPerSample";
     field public static final java.lang.String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
     field public static final java.lang.String TAG_CFA_PATTERN = "CFAPattern";
@@ -20297,6 +20298,8 @@
     field public static final java.lang.String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
     field public static final java.lang.String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
     field public static final java.lang.String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
+    field public static final java.lang.String TAG_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
+    field public static final java.lang.String TAG_PREVIEW_IMAGE_START = "PreviewImageStart";
     field public static final java.lang.String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
     field public static final java.lang.String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
     field public static final java.lang.String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
@@ -20324,6 +20327,7 @@
     field public static final java.lang.String TAG_SUBSEC_TIME_DIGITIZED = "SubSecTimeDigitized";
     field public static final deprecated java.lang.String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
     field public static final java.lang.String TAG_SUBSEC_TIME_ORIGINAL = "SubSecTimeOriginal";
+    field public static final java.lang.String TAG_THUMBNAIL_IMAGE = "ThumbnailImage";
     field public static final java.lang.String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
     field public static final java.lang.String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
     field public static final java.lang.String TAG_TRANSFER_FUNCTION = "TransferFunction";
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index fc3596e..83e2678 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -165,10 +165,10 @@
             int userId);
 
     /**
-     * Start an activity {@code intent} as if {@code packageName} on user {@code userId} did it.
+     * Start activity {@code intents} as if {@code packageName} on user {@code userId} did it.
      *
      * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
      */
-    public abstract int startActivityAsPackage(String packageName,
-            int userId, Intent intent, Bundle bOptions);
+    public abstract int startActivitiesAsPackage(String packageName,
+            int userId, Intent[] intents, Bundle bOptions);
 }
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 54f1b76..0ec1623 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -182,16 +182,16 @@
     private ArraySet<String> mCategories;
 
     /**
-     * Intent *with extras removed*.
+     * Intents *with extras removed*.
      */
     @Nullable
-    private Intent mIntent;
+    private Intent[] mIntents;
 
     /**
-     * Extras for the intent.
+     * Extras for the intents.
      */
     @Nullable
-    private PersistableBundle mIntentPersistableExtras;
+    private PersistableBundle[] mIntentPersistableExtrases;
 
     private int mRank;
 
@@ -241,20 +241,36 @@
         mDisabledMessage = b.mDisabledMessage;
         mDisabledMessageResId = b.mDisabledMessageResId;
         mCategories = cloneCategories(b.mCategories);
-        mIntent = b.mIntent;
-        if (mIntent != null) {
-            final Bundle intentExtras = mIntent.getExtras();
-            if (intentExtras != null) {
-                mIntent.replaceExtras((Bundle) null);
-                mIntentPersistableExtras = new PersistableBundle(intentExtras);
-            }
-        }
+        mIntents = cloneIntents(b.mIntents);
+        fixUpIntentExtras();
         mRank = b.mRank;
         mExtras = b.mExtras;
         updateTimestamp();
     }
 
-    private ArraySet<String> cloneCategories(Set<String> source) {
+    /**
+     * Extract extras from {@link #mIntents} and set them to {@link #mIntentPersistableExtrases}
+     * as {@link PersistableBundle}, and remove extras from the original intents.
+     */
+    private void fixUpIntentExtras() {
+        if (mIntents == null) {
+            mIntentPersistableExtrases = null;
+            return;
+        }
+        mIntentPersistableExtrases = new PersistableBundle[mIntents.length];
+        for (int i = 0; i < mIntents.length; i++) {
+            final Intent intent = mIntents[i];
+            final Bundle extras = intent.getExtras();
+            if (extras == null) {
+                mIntentPersistableExtrases[i] = null;
+            } else {
+                mIntentPersistableExtrases[i] = new PersistableBundle(extras);
+                intent.replaceExtras((Bundle) null);
+            }
+        }
+    }
+
+    private static ArraySet<String> cloneCategories(Set<String> source) {
         if (source == null) {
             return null;
         }
@@ -267,6 +283,32 @@
         return ret;
     }
 
+    private static Intent[] cloneIntents(Intent[] intents) {
+        if (intents == null) {
+            return null;
+        }
+        final Intent[] ret = new Intent[intents.length];
+        for (int i = 0; i < ret.length; i++) {
+            if (intents[i] != null) {
+                ret[i] = new Intent(intents[i]);
+            }
+        }
+        return ret;
+    }
+
+    private static PersistableBundle[] clonePersistableBundle(PersistableBundle[] bundle) {
+        if (bundle == null) {
+            return null;
+        }
+        final PersistableBundle[] ret = new PersistableBundle[bundle.length];
+        for (int i = 0; i < ret.length; i++) {
+            if (bundle[i] != null) {
+                ret[i] = new PersistableBundle(bundle[i]);
+            }
+        }
+        return ret;
+    }
+
     /**
      * Throws if any of the mandatory fields is not set.
      *
@@ -278,7 +320,8 @@
         if (mTitle == null && mTitleResId == 0) {
             throw new IllegalArgumentException("Short label must be provided");
         }
-        Preconditions.checkNotNull(mIntent, "Shortcut Intent must be provided");
+        Preconditions.checkNotNull(mIntents, "Shortcut Intent must be provided");
+        Preconditions.checkArgument(mIntents.length > 0, "Shortcut Intent must be provided");
     }
 
     /**
@@ -310,8 +353,9 @@
             mDisabledMessageResId = source.mDisabledMessageResId;
             mCategories = cloneCategories(source.mCategories);
             if ((cloneFlags & CLONE_REMOVE_INTENT) == 0) {
-                mIntent = source.mIntent;
-                mIntentPersistableExtras = source.mIntentPersistableExtras;
+                mIntents = cloneIntents(source.mIntents);
+                mIntentPersistableExtrases =
+                        clonePersistableBundle(source.mIntentPersistableExtrases);
             }
             mRank = source.mRank;
             mExtras = source.mExtras;
@@ -620,9 +664,10 @@
         if (source.mCategories != null) {
             mCategories = cloneCategories(source.mCategories);
         }
-        if (source.mIntent != null) {
-            mIntent = source.mIntent;
-            mIntentPersistableExtras = source.mIntentPersistableExtras;
+        if (source.mIntents != null) {
+            mIntents = cloneIntents(source.mIntents);
+            mIntentPersistableExtrases =
+                    clonePersistableBundle(source.mIntentPersistableExtrases);
         }
         if (source.mRank != RANK_NOT_SET) {
             mRank = source.mRank;
@@ -684,7 +729,7 @@
 
         private Set<String> mCategories;
 
-        private Intent mIntent;
+        private Intent[] mIntents;
 
         private int mRank = RANK_NOT_SET;
 
@@ -912,12 +957,11 @@
          * supported so the system can persist them.
          *
          * @see ShortcutInfo#getIntent()
+         * @see #setIntents(Intent[])
          */
         @NonNull
         public Builder setIntent(@NonNull Intent intent) {
-            mIntent = Preconditions.checkNotNull(intent, "intent cannot be null");
-            Preconditions.checkNotNull(mIntent.getAction(), "intent's action must be set");
-            return this;
+            return setIntents(new Intent[]{intent});
         }
 
         /**
@@ -930,7 +974,15 @@
          */
         @NonNull
         public Builder setIntents(@NonNull Intent[] intents) {
-            throw new RuntimeException("NOT SUPPORTED YET");
+            Preconditions.checkNotNull(intents, "intents cannot be null");
+            Preconditions.checkNotNull(intents.length, "intents cannot be empty");
+            for (Intent intent : intents) {
+                Preconditions.checkNotNull(intent, "intents cannot contain null");
+                Preconditions.checkNotNull(intent.getAction(), "intent's action must be set");
+            }
+            // Make sure always clone incoming intents.
+            mIntents = cloneIntents(intents);
+            return this;
         }
 
         /**
@@ -1098,7 +1150,7 @@
     }
 
     /**
-     * Return the intent.  (Or the last intent set with {@link Builder#setIntents(Intent[])}.
+     * Return the intent. If setIntents() was used, then return the last intent in the array.
      *
      * <p>Launcher applications <b>cannot</b> see the intent.  If a {@link ShortcutInfo} is
      * obtained via {@link LauncherApps}, then this method will always return null.
@@ -1108,13 +1160,12 @@
      */
     @Nullable
     public Intent getIntent() {
-        if (mIntent == null) {
+        if (mIntents == null || mIntents.length == 0) {
             return null;
         }
-        final Intent intent = new Intent(mIntent);
-        intent.replaceExtras(
-                mIntentPersistableExtras != null ? new Bundle(mIntentPersistableExtras) : null);
-        return intent;
+        final int last = mIntents.length - 1;
+        final Intent intent = new Intent(mIntents[last]);
+        return setIntentExtras(intent, mIntentPersistableExtrases[last]);
     }
 
     /**
@@ -1127,27 +1178,34 @@
      * @see Builder#setIntents(Intent[])
      */
     @Nullable
-    public Intent getIntents() {
-        throw new RuntimeException("NOT SUPPORTED YET");
+    public Intent[] getIntents() {
+        final Intent[] ret = new Intent[mIntents.length];
+
+        for (int i = 0; i < ret.length; i++) {
+            ret[i] = new Intent(mIntents[i]);
+            setIntentExtras(ret[i], mIntentPersistableExtrases[i]);
+        }
+
+        return ret;
     }
 
     /**
-     * Return "raw" intent, which is the original intent without the extras.
+     * Return "raw" intents, which is the original intents without the extras.
      * @hide
      */
     @Nullable
-    public Intent getIntentNoExtras() {
-        return mIntent;
+    public Intent[] getIntentsNoExtras() {
+        return mIntents;
     }
 
     /**
-     * The extras in the intent.  We convert extras into {@link PersistableBundle} so we can
+     * The extras in the intents.  We convert extras into {@link PersistableBundle} so we can
      * persist them.
      * @hide
      */
     @Nullable
-    public PersistableBundle getIntentPersistableExtras() {
-        return mIntentPersistableExtras;
+    public PersistableBundle[] getIntentPersistableExtrases() {
+        return mIntentPersistableExtrases;
     }
 
     /**
@@ -1500,19 +1558,22 @@
      *
      * @hide
      */
-    public void setIntent(Intent intent) throws IllegalArgumentException {
-        Preconditions.checkNotNull(intent);
+    public void setIntents(Intent[] intents) throws IllegalArgumentException {
+        Preconditions.checkNotNull(intents);
+        Preconditions.checkArgument(intents.length > 0);
 
-        final Bundle intentExtras = intent.getExtras();
+        mIntents = cloneIntents(intents);
+        fixUpIntentExtras();
+    }
 
-        mIntent = intent;
-
-        if (intentExtras != null) {
+    /** @hide */
+    public static Intent setIntentExtras(Intent intent, PersistableBundle extras) {
+        if (extras == null) {
             intent.replaceExtras((Bundle) null);
-            mIntentPersistableExtras = new PersistableBundle(intentExtras);
         } else {
-            mIntentPersistableExtras = null;
+            intent.replaceExtras(new Bundle(extras));
         }
+        return intent;
     }
 
     /**
@@ -1546,8 +1607,8 @@
         mTextResId = source.readInt();
         mDisabledMessage = source.readCharSequence();
         mDisabledMessageResId = source.readInt();
-        mIntent = source.readParcelable(cl);
-        mIntentPersistableExtras = source.readParcelable(cl);
+        mIntents = source.readParcelableArray(cl, Intent.class);
+        mIntentPersistableExtrases = source.readParcelableArray(cl, PersistableBundle.class);
         mRank = source.readInt();
         mExtras = source.readParcelable(cl);
         mBitmapPath = source.readString();
@@ -1592,8 +1653,8 @@
         dest.writeCharSequence(mDisabledMessage);
         dest.writeInt(mDisabledMessageResId);
 
-        dest.writeParcelable(mIntent, flags);
-        dest.writeParcelable(mIntentPersistableExtras, flags);
+        dest.writeParcelableArray(mIntents, flags);
+        dest.writeParcelableArray(mIntentPersistableExtrases, flags);
         dest.writeInt(mRank);
         dest.writeParcelable(mExtras, flags);
         dest.writeString(mBitmapPath);
@@ -1723,11 +1784,27 @@
         sb.append(", timestamp=");
         sb.append(mLastChangedTimestamp);
 
-        sb.append(", intent=");
-        sb.append(mIntent);
-
-        sb.append(", intentExtras=");
-        sb.append(secure ? "***" : mIntentPersistableExtras);
+        sb.append(", intents=");
+        if (mIntents == null) {
+            sb.append("null");
+        } else {
+            if (secure) {
+                sb.append("size:");
+                sb.append(mIntents.length);
+            } else {
+                final int size = mIntents.length;
+                sb.append("[");
+                String sep = "";
+                for (int i = 0; i < size; i++) {
+                    sb.append(sep);
+                    sep = ", ";
+                    sb.append(mIntents[i]);
+                    sb.append("/");
+                    sb.append(mIntentPersistableExtrases[i]);
+                }
+                sb.append("]");
+            }
+        }
 
         sb.append(", extras=");
         sb.append(mExtras);
@@ -1754,9 +1831,8 @@
             Icon icon, CharSequence title, int titleResId, String titleResName,
             CharSequence text, int textResId, String textResName,
             CharSequence disabledMessage, int disabledMessageResId, String disabledMessageResName,
-            Set<String> categories,
-            Intent intent, PersistableBundle intentPersistableExtras,
-            int rank, PersistableBundle extras, long lastChangedTimestamp,
+            Set<String> categories, Intent[] intentsWithExtras, int rank, PersistableBundle extras,
+            long lastChangedTimestamp,
             int flags, int iconResId, String iconResName, String bitmapPath) {
         mUserId = userId;
         mId = id;
@@ -1773,8 +1849,8 @@
         mDisabledMessageResId = disabledMessageResId;
         mDisabledMessageResName = disabledMessageResName;
         mCategories = cloneCategories(categories);
-        mIntent = intent;
-        mIntentPersistableExtras = intentPersistableExtras;
+        mIntents = cloneIntents(intentsWithExtras);
+        fixUpIntentExtras();
         mRank = rank;
         mExtras = extras;
         mLastChangedTimestamp = lastChangedTimestamp;
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index f994d7e..af56105 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -53,7 +53,8 @@
             @NonNull String callingPackage, @NonNull String packageName,
             @NonNull List<String> shortcutIds, int userId);
 
-    public abstract Intent createShortcutIntent(int launcherUserId, @NonNull String callingPackage,
+    public abstract Intent[] createShortcutIntents(
+            int launcherUserId, @NonNull String callingPackage,
             @NonNull String packageName, @NonNull String shortcutId, int userId);
 
     public abstract void addListener(@NonNull ShortcutChangeListener listener);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 74dcc07..f6e6ad6 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -36,6 +36,7 @@
 import java.io.ObjectOutputStream;
 import java.io.ObjectStreamClass;
 import java.io.Serializable;
+import java.lang.reflect.Array;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
@@ -2571,6 +2572,20 @@
         return p;
     }
 
+    /** @hide */
+    public final <T extends Parcelable> T[] readParcelableArray(ClassLoader loader,
+            Class<T> clazz) {
+        int N = readInt();
+        if (N < 0) {
+            return null;
+        }
+        T[] p = (T[]) Array.newInstance(clazz, N);
+        for (int i = 0; i < N; i++) {
+            p[i] = readParcelable(loader);
+        }
+        return p;
+    }
+
     /**
      * Read and return a new Serializable object from the parcel.
      * @return the Serializable object, or null if the Serializable name
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4f1b839..0b198cc 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6265,19 +6265,35 @@
                 "automatic_storage_manager_last_run";
 
         /**
-         * Whether Downloads folder backup is enabled and should run on the device.
-         *
-         * @hide
-         */
-        public static final String DOWNLOADS_BACKUP_ENABLED = "downloads_backup_enabled";
-
-        /**
          * Whether SystemUI navigation keys is enabled.
          * @hide
          */
         public static final String SYSTEM_NAVIGATION_KEYS_ENABLED =
                 "system_navigation_keys_enabled";
 
+        /**
+         * Whether Downloads folder backup is enabled and should run on the device.
+         *
+         * @hide
+         */
+        public static final String DOWNLOADS_BACKUP_ENABLED = "downloads_backup_enabled";
+        
+        /**
+         * Whether Downloads folder backup should only occur if the device is using a metered
+         * network.
+         *
+         * @hide
+         */
+        public static final String DOWNLOADS_BACKUP_ALLOW_METERED =
+                "downloads_backup_allow_metered";
+
+        /**
+         * Whether Downloads folder backup should only occur if the device is charging.
+         *
+         * @hide
+         */
+        public static final String DOWNLOADS_BACKUP_CHARGING_ONLY =
+                "downloads_backup_charging_only";
 
         /**
          * This are the settings to be backed up.
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index b65f933..66b22ce 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2997,8 +2997,9 @@
             setChildrenDrawingCacheEnabled(false);
             if (mFlingRunnable != null) {
                 removeCallbacks(mFlingRunnable);
-                // let the fling runnable report it's new state which
+                // let the fling runnable report its new state which
                 // should be idle
+                mFlingRunnable.mSuppressIdleStateChangeCall = false;
                 mFlingRunnable.endFling();
                 if (mPositionScroller != null) {
                     mPositionScroller.stop();
@@ -4549,6 +4550,12 @@
          */
         private int mLastFlingY;
 
+        /**
+         * If true, {@link #endFling()} will not report scroll state change to
+         * {@link OnScrollListener#SCROLL_STATE_IDLE}.
+         */
+        private boolean mSuppressIdleStateChangeCall;
+
         private final Runnable mCheckFlywheel = new Runnable() {
             @Override
             public void run() {
@@ -4587,6 +4594,7 @@
             mScroller.fling(0, initialY, 0, initialVelocity,
                     0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE);
             mTouchMode = TOUCH_MODE_FLING;
+            mSuppressIdleStateChangeCall = false;
             postOnAnimation(this);
 
             if (PROFILE_FLINGING) {
@@ -4602,6 +4610,7 @@
         }
 
         void startSpringback() {
+            mSuppressIdleStateChangeCall = false;
             if (mScroller.springBack(0, mScrollY, 0, 0, 0, 0)) {
                 mTouchMode = TOUCH_MODE_OVERFLING;
                 invalidate();
@@ -4617,6 +4626,7 @@
             mScroller.fling(0, mScrollY, 0, initialVelocity, 0, 0,
                     Integer.MIN_VALUE, Integer.MAX_VALUE, 0, getHeight());
             mTouchMode = TOUCH_MODE_OVERFLING;
+            mSuppressIdleStateChangeCall = false;
             invalidate();
             postOnAnimation(this);
         }
@@ -4643,12 +4653,14 @@
             postOnAnimation(this);
         }
 
-        void startScroll(int distance, int duration, boolean linear) {
+        void startScroll(int distance, int duration, boolean linear,
+                boolean suppressEndFlingStateChangeCall) {
             int initialY = distance < 0 ? Integer.MAX_VALUE : 0;
             mLastFlingY = initialY;
             mScroller.setInterpolator(linear ? sLinearInterpolator : null);
             mScroller.startScroll(0, initialY, 0, distance, duration);
             mTouchMode = TOUCH_MODE_FLING;
+            mSuppressIdleStateChangeCall = suppressEndFlingStateChangeCall;
             postOnAnimation(this);
         }
 
@@ -4658,7 +4670,9 @@
             removeCallbacks(this);
             removeCallbacks(mCheckFlywheel);
 
-            reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
+            if (!mSuppressIdleStateChangeCall) {
+                reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
+            }
             clearScrollingCache();
             mScroller.abortAnimation();
 
@@ -4901,10 +4915,11 @@
      * @param duration Duration of the scroll animation in milliseconds.
      */
     public void smoothScrollBy(int distance, int duration) {
-        smoothScrollBy(distance, duration, false);
+        smoothScrollBy(distance, duration, false, false);
     }
 
-    void smoothScrollBy(int distance, int duration, boolean linear) {
+    void smoothScrollBy(int distance, int duration, boolean linear,
+            boolean suppressEndFlingStateChangeCall) {
         if (mFlingRunnable == null) {
             mFlingRunnable = new FlingRunnable();
         }
@@ -4926,7 +4941,7 @@
             }
         } else {
             reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
-            mFlingRunnable.startScroll(distance, duration, linear);
+            mFlingRunnable.startScroll(distance, duration, linear, suppressEndFlingStateChangeCall);
         }
     }
 
@@ -5000,6 +5015,9 @@
     /**
      * Scrolls the list items within the view by a specified number of pixels.
      *
+     * <p>The actual amount of scroll is capped by the list content viewport height
+     * which is the list height minus top and bottom paddings minus one pixel.</p>
+     *
      * @param y the amount of pixels to scroll by vertically
      * @see #canScrollList(int)
      */
@@ -7361,7 +7379,7 @@
             } else {
                 // On-screen, just scroll.
                 final int targetTop = getChildAt(mTargetPos - firstPos).getTop();
-                smoothScrollBy(targetTop - offset, duration, true);
+                smoothScrollBy(targetTop - offset, duration, true, false);
                 return;
             }
 
@@ -7461,7 +7479,7 @@
                         Math.max(mListPadding.bottom, mExtraScroll) : mListPadding.bottom;
 
                 final int scrollBy = lastViewHeight - lastViewPixelsShowing + extraScroll;
-                smoothScrollBy(scrollBy, mScrollDuration, true);
+                smoothScrollBy(scrollBy, mScrollDuration, true, lastPos < mTargetPos);
 
                 mLastSeenPos = lastPos;
                 if (lastPos < mTargetPos) {
@@ -7476,6 +7494,7 @@
 
                 if (firstPos == mBoundPos || childCount <= nextViewIndex
                         || firstPos + childCount >= mItemCount) {
+                    reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
                     return;
                 }
                 final int nextPos = firstPos + nextViewIndex;
@@ -7492,14 +7511,16 @@
                 final int extraScroll = Math.max(mListPadding.bottom, mExtraScroll);
                 if (nextPos < mBoundPos) {
                     smoothScrollBy(Math.max(0, nextViewHeight + nextViewTop - extraScroll),
-                            mScrollDuration, true);
+                            mScrollDuration, true, true);
 
                     mLastSeenPos = nextPos;
 
                     postOnAnimation(this);
                 } else  {
                     if (nextViewTop > extraScroll) {
-                        smoothScrollBy(nextViewTop - extraScroll, mScrollDuration, true);
+                        smoothScrollBy(nextViewTop - extraScroll, mScrollDuration, true, false);
+                    } else {
+                        reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
                     }
                 }
                 break;
@@ -7520,7 +7541,8 @@
                 final int extraScroll = firstPos > 0 ?
                         Math.max(mExtraScroll, mListPadding.top) : mListPadding.top;
 
-                smoothScrollBy(firstViewTop - extraScroll, mScrollDuration, true);
+                smoothScrollBy(firstViewTop - extraScroll, mScrollDuration, true,
+                        firstPos > mTargetPos);
 
                 mLastSeenPos = firstPos;
 
@@ -7550,13 +7572,16 @@
                 final int extraScroll = Math.max(mListPadding.top, mExtraScroll);
                 mLastSeenPos = lastPos;
                 if (lastPos > mBoundPos) {
-                    smoothScrollBy(-(lastViewPixelsShowing - extraScroll), mScrollDuration, true);
+                    smoothScrollBy(-(lastViewPixelsShowing - extraScroll), mScrollDuration, true,
+                            true);
                     postOnAnimation(this);
                 } else {
                     final int bottom = listHeight - extraScroll;
                     final int lastViewBottom = lastViewTop + lastViewHeight;
                     if (bottom > lastViewBottom) {
-                        smoothScrollBy(-(bottom - lastViewBottom), mScrollDuration, true);
+                        smoothScrollBy(-(bottom - lastViewBottom), mScrollDuration, true, false);
+                    } else {
+                        reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
                     }
                 }
                 break;
@@ -7589,12 +7614,12 @@
                 if (position < firstPos) {
                     final int distance = (int) (-getHeight() * modifier);
                     final int duration = (int) (mScrollDuration * modifier);
-                    smoothScrollBy(distance, duration, true);
+                    smoothScrollBy(distance, duration, true, true);
                     postOnAnimation(this);
                 } else if (position > lastPos) {
                     final int distance = (int) (getHeight() * modifier);
                     final int duration = (int) (mScrollDuration * modifier);
-                    smoothScrollBy(distance, duration, true);
+                    smoothScrollBy(distance, duration, true, true);
                     postOnAnimation(this);
                 } else {
                     // On-screen, just scroll.
@@ -7602,7 +7627,7 @@
                     final int distance = targetTop - mOffsetFromTop;
                     final int duration = (int) (mScrollDuration *
                             ((float) Math.abs(distance) / getHeight()));
-                    smoothScrollBy(distance, duration, true);
+                    smoothScrollBy(distance, duration, true, false);
                 }
                 break;
             }
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 9dd0d44..ad27c7c 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -188,7 +188,7 @@
     virtual void prepareTree(TreeInfo& info) override {
         info.errorHandler = this;
 
-        for (auto& anim : mVectorDrawableAnimators) {
+        for (auto& anim : mRunningVDAnimators) {
             // Assume that the property change in VD from the animators will not be consumed. Mark
             // otherwise if the VDs are found in the display list tree. For VDs that are not in
             // the display list tree, we stop providing animation pulses by 1) removing them from
@@ -196,6 +196,11 @@
             // listeners can receive the corresponding callbacks.
             anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false);
         }
+        if (info.mode == TreeInfo::MODE_FULL) {
+            for (auto &anim : mPausedVDAnimators) {
+                anim->getVectorDrawable()->setPropertyChangeWillBeConsumed(false);
+            }
+        }
         // TODO: This is hacky
         info.windowInsetLeft = -stagingProperties().getLeft();
         info.windowInsetTop = -stagingProperties().getTop();
@@ -206,17 +211,33 @@
         info.windowInsetTop = 0;
         info.errorHandler = nullptr;
 
-        for (auto it = mVectorDrawableAnimators.begin(); it != mVectorDrawableAnimators.end();) {
+        for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) {
             if (!(*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) {
                 // Vector Drawable is not in the display list, we should remove this animator from
-                // the list and post a delayed message to end the animator.
+                // the list, put it in the paused list, and post a delayed message to end the
+                // animator.
                 detachVectorDrawableAnimator(it->get());
-                it = mVectorDrawableAnimators.erase(it);
+                mPausedVDAnimators.insert(*it);
+                it = mRunningVDAnimators.erase(it);
             } else {
-                ++it;
+                it++;
             }
         }
-        info.out.hasAnimations |= !mVectorDrawableAnimators.empty();
+
+        if (info.mode == TreeInfo::MODE_FULL) {
+            // Check whether any paused animator's target is back in Display List. If so, put the
+            // animator back in the running list.
+            for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) {
+                if ((*it)->getVectorDrawable()->getPropertyChangeWillBeConsumed()) {
+                    mRunningVDAnimators.insert(*it);
+                    it = mPausedVDAnimators.erase(it);
+                } else {
+                    it++;
+                }
+            }
+        }
+        info.out.hasAnimations |= !mRunningVDAnimators.empty();
+
     }
 
     void sendMessage(const sp<MessageHandler>& handler) {
@@ -232,17 +253,18 @@
     }
 
     void attachPendingVectorDrawableAnimators() {
-        mVectorDrawableAnimators.insert(mPendingVectorDrawableAnimators.begin(),
+        mRunningVDAnimators.insert(mPendingVectorDrawableAnimators.begin(),
                 mPendingVectorDrawableAnimators.end());
         mPendingVectorDrawableAnimators.clear();
     }
 
     void detachAnimators() {
         // Remove animators from the list and post a delayed message in future to end the animator
-        for (auto& anim : mVectorDrawableAnimators) {
+        for (auto& anim : mRunningVDAnimators) {
             detachVectorDrawableAnimator(anim.get());
         }
-        mVectorDrawableAnimators.clear();
+        mRunningVDAnimators.clear();
+        mPausedVDAnimators.clear();
     }
 
     void doAttachAnimatingNodes(AnimationContext* context) {
@@ -253,18 +275,48 @@
         mPendingAnimatingRenderNodes.clear();
     }
 
-    void runVectorDrawableAnimators(AnimationContext* context) {
-        for (auto it = mVectorDrawableAnimators.begin(); it != mVectorDrawableAnimators.end();) {
+    void runVectorDrawableAnimators(AnimationContext* context, TreeInfo::TraversalMode mode) {
+        for (auto it = mRunningVDAnimators.begin(); it != mRunningVDAnimators.end();) {
             if ((*it)->animate(*context)) {
-                it = mVectorDrawableAnimators.erase(it);
+                it = mRunningVDAnimators.erase(it);
             } else {
-                ++it;
+                it++;
+            }
+        }
+
+        if (mode == TreeInfo::MODE_FULL) {
+            // During full sync we also need to pulse paused animators, in case their targets
+            // have been added back to the display list. All the animators that passed the
+            // scheduled finish time will be removed from the paused list.
+            for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) {
+                if ((*it)->animate(*context)) {
+                    // Animator has finished, remove from the list.
+                    it = mPausedVDAnimators.erase(it);
+                } else {
+                    it++;
+                }
+            }
+        }
+    }
+
+    void trimPausedVDAnimators(AnimationContext* context) {
+        // Trim paused vector drawable animator list.
+        for (auto it = mPausedVDAnimators.begin(); it != mPausedVDAnimators.end();) {
+            // Remove paused VD animator if no one else is referencing it. Note that animators that
+            // have passed scheduled finish time are removed from list when they are being pulsed
+            // before prepare tree.
+            // TODO: this is a bit hacky, need to figure out a better way to track when the paused
+            // animators should be freed.
+            if ((*it)->getStrongCount() == 1) {
+                it = mPausedVDAnimators.erase(it);
+            } else {
+                it++;
             }
         }
     }
 
     void pushStagingVectorDrawableAnimators(AnimationContext* context) {
-        for (auto& anim : mVectorDrawableAnimators) {
+        for (auto& anim : mRunningVDAnimators) {
             anim->pushStaging(*context);
         }
     }
@@ -286,7 +338,15 @@
     JavaVM* mVm;
     std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes;
     std::set< sp<PropertyValuesAnimatorSet> > mPendingVectorDrawableAnimators;
-    std::set< sp<PropertyValuesAnimatorSet> > mVectorDrawableAnimators;
+    std::set< sp<PropertyValuesAnimatorSet> > mRunningVDAnimators;
+    // mPausedVDAnimators stores a list of animators that have not yet passed the finish time, but
+    // their VectorDrawable targets are no longer in the DisplayList. We skip these animators when
+    // render thread runs animators independent of UI thread (i.e. RT_ONLY mode). These animators
+    // need to be re-activated once their VD target is added back into DisplayList. Since that could
+    // only happen when we do a full sync, we need to make sure to pulse these paused animators at
+    // full sync. If any animator's VD target is found in DisplayList during a full sync, we move
+    // the animator back to the running list.
+    std::set< sp<PropertyValuesAnimatorSet> > mPausedVDAnimators;
     void detachVectorDrawableAnimator(PropertyValuesAnimatorSet* anim) {
         if (anim->isInfinite() || !anim->isRunning()) {
             // Do not need to post anything if the animation is infinite (i.e. no meaningful
@@ -336,13 +396,20 @@
         if (mode == TreeInfo::MODE_FULL) {
             mRootNode->pushStagingVectorDrawableAnimators(this);
         }
-        mRootNode->runVectorDrawableAnimators(this);
+        mRootNode->runVectorDrawableAnimators(this, mode);
     }
 
     // Runs any animations still left in mCurrentFrameAnimations
     virtual void runRemainingAnimations(TreeInfo& info) {
         AnimationContext::runRemainingAnimations(info);
         postOnFinishedEvents();
+        if (info.mode == TreeInfo::MODE_FULL) {
+            // Trim paused VD animators at full sync, so that when Java loses reference to an
+            // animator, we know we won't be requested to animate it any more, then we remove such
+            // animators from the paused list so they can be properly freed. We also remove the
+            // animators from paused list when the time elapsed since start has exceeded duration.
+            mRootNode->trimPausedVDAnimators(this);
+        }
     }
 
     virtual void detachAnimators() override {
diff --git a/core/res/res/values-w194dp/dimens_material.xml b/core/res/res/values-w192dp/dimens_material.xml
similarity index 81%
rename from core/res/res/values-w194dp/dimens_material.xml
rename to core/res/res/values-w192dp/dimens_material.xml
index 220c4b0..797bf5a 100644
--- a/core/res/res/values-w194dp/dimens_material.xml
+++ b/core/res/res/values-w192dp/dimens_material.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 <resources>
-    <dimen name="screen_percentage_05">9.7dp</dimen>
-    <dimen name="screen_percentage_10">19.4dp</dimen>
-    <dimen name="screen_percentage_15">29.1dp</dimen>
+    <dimen name="screen_percentage_05">9.6dp</dimen>
+    <dimen name="screen_percentage_10">19.2dp</dimen>
+    <dimen name="screen_percentage_15">28.8dp</dimen>
 </resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 10e2a4a..1843475 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -74,6 +74,8 @@
     <drawable name="input_method_fullscreen_background">#fff9f9f9</drawable>
     <color name="input_method_navigation_guard">#ff000000</color>
 
+    <color name="system_error">#fff4511e</color> <!-- deep orange 600 -->
+
     <!-- For date picker widget -->
     <drawable name="selected_day_background">#ff0092f4</drawable>
 
@@ -118,7 +120,7 @@
     <!-- LockPatternView -->
     <color name="lock_pattern_view_regular_color">#ffffffff</color>
     <color name="lock_pattern_view_success_color">#ffffffff</color>
-    <color name="lock_pattern_view_error_color">#fff4511e</color>
+    <color name="lock_pattern_view_error_color">@color/system_error</color>
 
     <!-- FaceLock -->
     <color name="facelock_spotlight_mask">#CC000000</color>
diff --git a/docs/html/_project.yaml b/docs/html/_project.yaml
new file mode 100644
index 0000000..a67469b
--- /dev/null
+++ b/docs/html/_project.yaml
@@ -0,0 +1,5 @@
+name: "Home"
+home_url: /
+description: "Android Developers website."
+content_license: cc3-apache2
+buganizer_id: 30209417
diff --git a/docs/html/about/_project.yaml b/docs/html/about/_project.yaml
new file mode 100644
index 0000000..160cf55
--- /dev/null
+++ b/docs/html/about/_project.yaml
@@ -0,0 +1,5 @@
+name: "Android"
+home_url: /about/
+description: "Android, the world's most popular mobile platform"
+content_license: cc3-apache2
+buganizer_id: 30209417
diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd
index a9f1985..3cbfde9 100644
--- a/docs/html/about/dashboards/index.jd
+++ b/docs/html/about/dashboards/index.jd
@@ -381,7 +381,7 @@
     $divtable.append($table);
     $divchart.append($chart);
     $div.append($divtable).append($divchart);
-    $("#version-chart").append($div);
+    $("#version-chart").replaceWith($div);
   });
 
 
@@ -449,7 +449,7 @@
 
     // stack up and insert the elements
     $div.append($table).append($sizechart).append($densitychart);
-    $("#screens-chart").append($div);
+    $("#screens-chart").replaceWith($div);
   });
 
   // TODO (akassay): Remove this.
diff --git a/docs/html/design/_project.yaml b/docs/html/design/_project.yaml
new file mode 100644
index 0000000..72df4a8
--- /dev/null
+++ b/docs/html/design/_project.yaml
@@ -0,0 +1,5 @@
+name: "Design"
+home_url: /design/
+description: "Create beautiful experiences in your apps."
+content_license: cc3-apache2
+buganizer_id: 30209417
diff --git a/docs/html/develop/_project.yaml b/docs/html/develop/_project.yaml
new file mode 100644
index 0000000..40a66b3
--- /dev/null
+++ b/docs/html/develop/_project.yaml
@@ -0,0 +1,5 @@
+name: "Develop"
+home_url: /develop/
+description: "Android Studio provides the fastest tools for building apps on every type of Android device."
+content_license: cc3-apache2
+buganizer_id: 30209417
diff --git a/docs/html/distribute/_project.yaml b/docs/html/distribute/_project.yaml
new file mode 100644
index 0000000..5777333
--- /dev/null
+++ b/docs/html/distribute/_project.yaml
@@ -0,0 +1,5 @@
+name: "Distribute"
+home_url: /distribute/
+description: "The most visited store in the world for Android apps. Cloud-connected and always synced, it's never been easier for users to find and download your apps."
+content_license: cc3-apache2
+buganizer_id: 30209417
diff --git a/docs/html/distribute/analyze/_project.yaml b/docs/html/distribute/analyze/_project.yaml
new file mode 100644
index 0000000..5c77074
--- /dev/null
+++ b/docs/html/distribute/analyze/_project.yaml
@@ -0,0 +1,6 @@
+name: "Analyze"
+home_url: /distribute/analyze/
+description: "Understanding what your users do inside your app is the key to engaging and monetizing them."
+content_license: cc3-apache2
+buganizer_id: 30209417
+parent_project_metadata_path: /distribute/_project.yaml
diff --git a/docs/html/distribute/engage/_project.yaml b/docs/html/distribute/engage/_project.yaml
new file mode 100644
index 0000000..fd8fa44
--- /dev/null
+++ b/docs/html/distribute/engage/_project.yaml
@@ -0,0 +1,6 @@
+name: "Engage & Retain"
+home_url: /distribute/engage/
+description: "Engaging and retaining active users are the keys to success. Here are some resources to help you build an active user base."
+content_license: cc3-apache2
+buganizer_id: 30209417
+parent_project_metadata_path: /distribute/_project.yaml
diff --git a/docs/html/distribute/essentials/_project.yaml b/docs/html/distribute/essentials/_project.yaml
new file mode 100644
index 0000000..cafde8d
--- /dev/null
+++ b/docs/html/distribute/essentials/_project.yaml
@@ -0,0 +1,6 @@
+name: "Essentials"
+home_url: /distribute/essentials/
+description: "A focus on quality should be part of your entire app delivery process: from initial concept through app and UI design, coding and testing and onto a process of monitoring feedback and making improvement after launch."
+content_license: cc3-apache2
+buganizer_id: 30209417
+parent_project_metadata_path: /distribute/_project.yaml
diff --git a/docs/html/distribute/googleplay/_project.yaml b/docs/html/distribute/googleplay/_project.yaml
new file mode 100644
index 0000000..2dfded3
--- /dev/null
+++ b/docs/html/distribute/googleplay/_project.yaml
@@ -0,0 +1,6 @@
+name: "Google Play"
+home_url: /distribute/googleplay/
+description: "The premier store for distributing Android apps and games, with global reach and tools to help you gain traction in the marketplace."
+content_license: cc3-apache2
+buganizer_id: 30209417
+parent_project_metadata_path: /distribute/_project.yaml
diff --git a/docs/html/distribute/monetize/_project.yaml b/docs/html/distribute/monetize/_project.yaml
new file mode 100644
index 0000000..1936be2
--- /dev/null
+++ b/docs/html/distribute/monetize/_project.yaml
@@ -0,0 +1,6 @@
+name: "Earn"
+home_url: /distribute/monetize/
+description: "There are many ways to make money with your apps on Google Play, and we offer a variety of tools to make it easy."
+content_license: cc3-apache2
+buganizer_id: 30209417
+parent_project_metadata_path: /distribute/_project.yaml
diff --git a/docs/html/distribute/stories/_project.yaml b/docs/html/distribute/stories/_project.yaml
new file mode 100644
index 0000000..1c242bb
--- /dev/null
+++ b/docs/html/distribute/stories/_project.yaml
@@ -0,0 +1,6 @@
+name: "Stories"
+home_url: /distribute/stories/
+description: "Android developers, their apps, and their successes with Android and Google Play."
+content_license: cc3-apache2
+buganizer_id: 30209417
+parent_project_metadata_path: /distribute/_project.yaml
diff --git a/docs/html/distribute/tools/_project.yaml b/docs/html/distribute/tools/_project.yaml
new file mode 100644
index 0000000..a034ede
--- /dev/null
+++ b/docs/html/distribute/tools/_project.yaml
@@ -0,0 +1,6 @@
+name: "Tools & Reference"
+home_url: /distribute/tools/
+description: "Here you'll find resources to help you publish your apps and games, acquire users, and monetize your investment."
+content_license: cc3-apache2
+buganizer_id: 30209417
+parent_project_metadata_path: /distribute/_project.yaml
diff --git a/docs/html/distribute/users/_project.yaml b/docs/html/distribute/users/_project.yaml
new file mode 100644
index 0000000..cd7083e
--- /dev/null
+++ b/docs/html/distribute/users/_project.yaml
@@ -0,0 +1,6 @@
+name: "Get Users"
+home_url: /distribute/users/
+description: "There are some common themes from successful Google Play developers. These best practices are critical to your app or game's success."
+content_license: cc3-apache2
+buganizer_id: 30209417
+parent_project_metadata_path: /distribute/_project.yaml
diff --git a/docs/html/google/_project.yaml b/docs/html/google/_project.yaml
new file mode 100644
index 0000000..051e6bc
--- /dev/null
+++ b/docs/html/google/_project.yaml
@@ -0,0 +1,5 @@
+name: "Google Services"
+home_url: /google/
+description: "Take advantage of the latest Google technologies through a single set of APIs, delivered across Android devices worldwide as part of Google Play services."
+content_license: cc3-apache2
+buganizer_id: 30209417
diff --git a/docs/html/guide/_project.yaml b/docs/html/guide/_project.yaml
new file mode 100644
index 0000000..d1f8e5e
--- /dev/null
+++ b/docs/html/guide/_project.yaml
@@ -0,0 +1,6 @@
+name: "API Guides"
+home_url: /guide/
+description: "Android provides a rich application framework that allows you to build innovative apps and games for mobile devices in a Java language environment."
+content_license: cc3-apache2
+buganizer_id: 30209417
+parent_project_metadata_path: /develop/_project.yaml
diff --git a/docs/html/guide/topics/renderscript/advanced.jd b/docs/html/guide/topics/renderscript/advanced.jd
index 6a72b97..5cc0556 100644
--- a/docs/html/guide/topics/renderscript/advanced.jd
+++ b/docs/html/guide/topics/renderscript/advanced.jd
@@ -63,7 +63,7 @@
   <code>llvm</code> compiler that runs as part of an Android build. When your application
   runs on a device, the bytecode is then compiled (just-in-time) to machine code by another
   <code>llvm</code> compiler that resides on the device. The machine code is optimized for the
-  device and also cached, so subsequent uses of the RenderScript enabled application does not
+  device and also cached, so subsequent uses of the RenderScript enabled application do not
   recompile the bytecode.</p>
 
   <p>Some key features of the RenderScript runtime libraries include:</p>
@@ -128,7 +128,7 @@
 <h3 id="func">Functions</h3>
 <p>Functions are reflected into the script class itself, located in
 <code>project_root/gen/package/name/ScriptC_renderscript_filename</code>. For
-example, if you declare the following function in your RenderScript code:</p>
+example, if you define the following function in your RenderScript code:</p>
 
 <pre>
 void touch(float x, float y, float pressure, int id) {
@@ -142,7 +142,7 @@
 }
 </pre>
 
-<p>then the following code is generated:</p>
+<p>then the following Java code is generated:</p>
 
 <pre>
 public void invoke_touch(float x, float y, float pressure, int id) {
@@ -155,7 +155,7 @@
 }
 </pre>
 <p>
-Functions cannot have a return value, because the RenderScript system is designed to be
+Functions cannot have return values, because the RenderScript system is designed to be
 asynchronous. When your Android framework code calls into RenderScript, the call is queued and is
 executed when possible. This restriction allows the RenderScript system to function without constant
 interruption and increases efficiency. If functions were allowed to have return values, the call
@@ -171,11 +171,11 @@
 
   <p>Variables of supported types are reflected into the script class itself, located in
 <code>project_root/gen/package/name/ScriptC_renderscript_filename</code>. A set of accessor
-methods are generated for each variable. For example, if you declare the following variable in
+methods is generated for each variable. For example, if you define the following variable in
 your RenderScript code:</p>
   <pre>uint32_t unsignedInteger = 1;</pre>
 
-  <p>then the following code is generated:</p>
+  <p>then the following Java code is generated:</p>
 
 <pre>
 private long mExportVar_unsignedInteger;
@@ -194,7 +194,7 @@
   <p>Structs are reflected into their own classes, located in
     <code>&lt;project_root&gt;/gen/com/example/renderscript/ScriptField_struct_name</code>. This
     class represents an array of the <code>struct</code> and allows you to allocate memory for a
-    specified number of <code>struct</code>s. For example, if you declare the following struct:</p>
+    specified number of <code>struct</code>s. For example, if you define the following struct:</p>
 <pre>
 typedef struct Point {
     float2 position;
@@ -373,7 +373,7 @@
 the array. The RenderScript runtime automatically has access to the newly written memory.
 
       <li>Accessor methods to get and set the values of each field in a struct. Each of these
-        accessor methods have an <code>index</code> parameter to specify the <code>struct</code> in
+        accessor methods has an <code>index</code> parameter to specify the <code>struct</code> in
         the array that you want to read or write to. Each setter method also has a
 <code>copyNow</code> parameter that specifies whether or not to immediately sync this memory
 to the RenderScript runtime. To sync any memory that has not been synced, call
@@ -395,10 +395,10 @@
     </ul>
 
   <h3 id="pointer">Pointers</h3>
-  <p>Pointers are reflected into the script class itself, located in
+  <p>Global pointers are reflected into the script class itself, located in
 <code>project_root/gen/package/name/ScriptC_renderscript_filename</code>. You
 can declare pointers to a <code>struct</code> or any of the supported RenderScript types, but a
-<code>struct</code> cannot contain pointers or nested arrays. For example, if you declare the
+<code>struct</code> cannot contain pointers or nested arrays. For example, if you define the
 following pointers to a <code>struct</code> and <code>int32_t</code></p>
 
 <pre>
@@ -410,7 +410,7 @@
 Point_t *touchPoints;
 int32_t *intPointer;
 </pre>
-  <p>then the following code is generated in:</p>
+  <p>then the following Java code is generated:</p>
 
 <pre>
 private ScriptField_Point mExportVar_touchPoints;
@@ -437,7 +437,7 @@
   </pre>
 
 <p>A <code>get</code> method and a special method named <code>bind_<em>pointer_name</em></code>
-(instead of a <code>set()</code> method) is generated. This method allows you to bind the memory
+(instead of a <code>set()</code> method) are generated. The <code>bind_<em>pointer_name</em></code> method allows you to bind the memory
 that is allocated in the Android VM to the RenderScript runtime (you cannot allocate
 memory in your <code>.rs</code> file). For more information, see <a href="#memory">Working
 with Allocated Memory</a>.
@@ -521,7 +521,7 @@
         describes.</p>
 
         <p>A type consists of five dimensions: X, Y, Z, LOD (level of detail), and Faces (of a cube
-        map). You can assign the X,Y,Z dimensions to any positive integer value within the
+        map). You can set the X,Y,Z dimensions to any positive integer value within the
         constraints of available memory. A single dimension allocation has an X dimension of
         greater than zero while the Y and Z dimensions are zero to indicate not present. For
         example, an allocation of x=10, y=1 is considered two dimensional and x=10, y=0 is
diff --git a/docs/html/guide/topics/renderscript/compute.jd b/docs/html/guide/topics/renderscript/compute.jd
index 861c925..c5b49d7 100755
--- a/docs/html/guide/topics/renderscript/compute.jd
+++ b/docs/html/guide/topics/renderscript/compute.jd
@@ -16,6 +16,13 @@
         </ol>
       </li>
       <li><a href="#using-rs-from-java">Using RenderScript from Java Code</a></li>
+      <li><a href="#reduction-in-depth">Reduction Kernels in Depth</a>
+        <ol>
+          <li><a href="#writing-reduction-kernel">Writing a reduction kernel</a></li>
+          <li><a href="#calling-reduction-kernel">Calling a reduction kernel from Java code</a></li>
+          <li><a href="#more-example">More example reduction kernels</a></li>
+        </ol>
+      </li>
     </ol>
 
     <h2>Related Samples</h2>
@@ -29,16 +36,18 @@
 
 <p>RenderScript is a framework for running computationally intensive tasks at high performance on
 Android. RenderScript is primarily oriented for use with data-parallel computation, although serial
-computationally intensive workloads can benefit as well. The RenderScript runtime will parallelize
-work across all processors available on a device, such as multi-core CPUs, GPUs, or DSPs, allowing
-you to focus on expressing algorithms rather than scheduling work or load balancing. RenderScript is
+workloads can benefit as well. The RenderScript runtime parallelizes
+work across processors available on a device, such as multi-core CPUs and GPUs. This allows
+you to focus on expressing algorithms rather than scheduling work. RenderScript is
 especially useful for applications performing image processing, computational photography, or
 computer vision.</p>
 
 <p>To begin with RenderScript, there are two main concepts you should understand:</p>
 <ul>
 
-<li>High-performance compute kernels are written in a C99-derived language.</li>
+<li>High-performance compute kernels are written in a C99-derived language. A <i>compute
+    kernel</i> is a function or collection of functions that you can direct the RenderScript runtime
+    to execute in parallel across a collection of data.</li>
 
 <li>A Java API is used for managing the lifetime of RenderScript resources and controlling kernel
 execution.</li>
@@ -48,7 +57,7 @@
 
 <p>A RenderScript kernel typically resides in a <code>.rs</code> file in the
 <code>&lt;project_root&gt;/src/</code> directory; each <code>.rs</code> file is called a
-script. Every script contains its own set of kernels, functions, and variables. A script can
+<i>script</i>. Every script contains its own set of kernels, functions, and variables. A script can
 contain:</p>
 
 <ul>
@@ -57,23 +66,32 @@
 
 <li>A pragma declaration (<code>#pragma rs java_package_name(com.example.app)</code>) that
 declares the package name of the Java classes reflected from this script.
-Note that your .rs file must be part of your application package, and not in a
+Note that your <code>.rs</code> file must be part of your application package, and not in a
 library project.</li>
 
-<li>Some number of invokable functions. An invokable function is a single-threaded RenderScript
+<li>Zero or more <strong><i>invokable functions</i></strong>. An invokable function is a single-threaded RenderScript
 function that you can call from your Java code with arbitrary arguments. These are often useful for
 initial setup or serial computations within a larger processing pipeline.</li>
 
-<li>Some number of script globals. A script global is equivalent to a global variable in C. You can
+<li><p>Zero or more <strong><i>script globals</i></strong>. A script global is equivalent to a global variable in C. You can
 access script globals from Java code, and these are often used for parameter passing to RenderScript
-kernels.</li>
+kernels.</p></li>
 
-<li>Some number of compute kernels. A kernel is a parallel function that executes across every
-{@link android.renderscript.Element} within an {@link android.renderscript.Allocation}.
+<li><p>Zero or more <strong><i>compute kernels</i></strong>. There are two kinds of compute
+kernels: <i>mapping</i> kernels (also called <i>foreach</i> kernels)
+and <i>reduction</i> kernels.</p>
 
-<p>A simple kernel may look like the following:</p>
+<p>A <em>mapping kernel</em> is a parallel function that operates on a collection of {@link
+  android.renderscript.Allocation Allocations} of the same dimensions. By default, it executes
+  once for every coordinate in those dimensions. It is typically (but not exclusively) used to
+  transform a collection of input {@link android.renderscript.Allocation Allocations} to an
+  output {@link android.renderscript.Allocation} one {@link android.renderscript.Element} at a
+  time.</p>
 
-<pre>uchar4 __attribute__((kernel)) invert(uchar4 in, uint32_t x, uint32_t y) {
+<ul>
+<li><p>Here is an example of a simple <strong>mapping kernel</strong>:</p>
+
+<pre>uchar4 RS_KERNEL invert(uchar4 in, uint32_t x, uint32_t y) {
   uchar4 out = in;
   out.r = 255 - in.r;
   out.g = 255 - in.g;
@@ -81,40 +99,113 @@
   return out;
 }</pre>
 
-<p>In most respects, this is identical to a standard C function. The first notable feature is the
-<code>__attribute__((kernel))</code> applied to the function prototype. This denotes that the
-function is a RenderScript kernel instead of an invokable function. The next feature is the
-<code>in</code> argument and its type. In a RenderScript kernel, this is a special argument that is
-automatically filled in based on the input {@link android.renderscript.Allocation} passed to the
-kernel launch. By default, the kernel is run across an entire {@link
-android.renderscript.Allocation}, with one execution of the kernel body per {@link
-android.renderscript.Element} in the {@link android.renderscript.Allocation}. The third notable
-feature is the return type of the kernel. The value returned from the kernel is automatically
-written to the appropriate location in the output {@link android.renderscript.Allocation}. The
-RenderScript runtime checks to ensure that the {@link android.renderscript.Element} types of the
-input and output Allocations match the kernel's prototype; if they do not match, an exception is
-thrown.</p>
+<p>In most respects, this is identical to a standard C
+  function. The <a href="#RS_KERNEL"><code>RS_KERNEL</code></a> property applied to the
+  function prototype specifies that the function is a RenderScript mapping kernel instead of an
+  invokable function. The <code>in</code> argument is automatically filled in based on the
+  input {@link android.renderscript.Allocation} passed to the kernel launch. The
+  arguments <code>x</code> and <code>y</code> are
+  discussed <a href="#special-arguments">below</a>. The value returned from the kernel is
+  automatically written to the appropriate location in the output {@link
+  android.renderscript.Allocation}. By default, this kernel is run across its entire input
+  {@link android.renderscript.Allocation}, with one execution of the kernel function per {@link
+  android.renderscript.Element} in the {@link android.renderscript.Allocation}.</p>
 
-<p>A kernel may have an input {@link android.renderscript.Allocation}, an output {@link
-android.renderscript.Allocation}, or both. A kernel may not have more than one input or one output
-{@link android.renderscript.Allocation}. If more than one input or output is required, those objects
-should be bound to <code>rs_allocation</code> script globals and accessed from a kernel or invokable
-function via <code>rsGetElementAt_<em>type</em>()</code> or
-<code>rsSetElementAt_<em>type</em>()</code>.</p>
+<p>A mapping kernel may have one or more input {@link android.renderscript.Allocation
+  Allocations}, a single output {@link android.renderscript.Allocation}, or both. The
+  RenderScript runtime checks to ensure that all input and output Allocations have the same
+  dimensions, and that the {@link android.renderscript.Element} types of the input and output
+  Allocations match the kernel's prototype; if either of these checks fails, RenderScript
+  throws an exception.</p>
 
-<p>A kernel may access the coordinates of the current execution using the <code>x</code>,
-<code>y</code>, and <code>z</code> arguments. These arguments are optional, but the type of the
-coordinate arguments must be <code>uint32_t</code>.</p></li>
+<p class="note"><strong>NOTE:</strong> Before Android 6.0 (API level 23), a mapping kernel may
+  not have more than one input {@link android.renderscript.Allocation}.</p>
+
+<p>If you need more input or output {@link android.renderscript.Allocation Allocations} than
+  the kernel has, those objects should be bound to <code>rs_allocation</code> script globals
+  and accessed from a kernel or invokable function
+  via <code>rsGetElementAt_<i>type</i>()</code> or <code>rsSetElementAt_<i>type</i>()</code>.</p>
+
+<p><strong>NOTE:</strong> <a id="RS_KERNEL"><code>RS_KERNEL</code></a> is a macro
+  defined automatically by RenderScript for your convenience:</p>
+<pre>
+#define RS_KERNEL __attribute__((kernel))
+</pre>
+</li>
+</ul>
+
+<p>A <em>reduction kernel</em> is a family of functions that operates on a collection of input
+  {@link android.renderscript.Allocation Allocations} of the same dimensions. By default,
+  its <a href="#accumulator-function">accumulator function</a> executes once for every
+  coordinate in those dimensions.  It is typically (but not exclusively) used to "reduce" a
+  collection of input {@link android.renderscript.Allocation Allocations} to a single
+  value.</p>
+
+<ul>
+<li><p>Here is an <a id="example-addint">example</a> of a simple <strong>reduction
+kernel</strong> that adds up the {@link android.renderscript.Element Elements} of its
+input:</p>
+
+<pre>#pragma rs reduce(addint) accumulator(addintAccum)
+
+static void addintAccum(int *accum, int val) {
+  *accum += val;
+}</pre>
+
+<p>A reduction kernel consists of one or more user-written functions.
+<code>#pragma rs reduce</code> is used to define the kernel by specifying its name
+(<code>addint</code>, in this example) and the names and roles of the functions that make
+up the kernel (an <code>accumulator</code> function <code>addintAccum</code>, in this
+example). All such functions must be <code>static</code>. A reduction kernel always
+requires an <code>accumulator</code> function; it may also have other functions, depending
+on what you want the kernel to do.</p>
+
+<p>A reduction kernel accumulator function must return <code>void</code> and must have at least
+two arguments. The first argument (<code>accum</code>, in this example) is a pointer to
+an <i>accumulator data item</i> and the second (<code>val</code>, in this example) is
+automatically filled in based on the input {@link android.renderscript.Allocation} passed to
+the kernel launch. The accumulator data item is created by the RenderScript runtime; by
+default, it is initialized to zero. By default, this kernel is run across its entire input
+{@link android.renderscript.Allocation}, with one execution of the accumulator function per
+{@link android.renderscript.Element} in the {@link android.renderscript.Allocation}. By
+default, the final value of the accumulator data item is treated as the result of the
+reduction, and is returned to Java.  The RenderScript runtime checks to ensure that the {@link
+android.renderscript.Element} type of the input Allocation matches the accumulator function's
+prototype; if it does not match, RenderScript throws an exception.</p>
+
+<p>A reduction kernel has one or more input {@link android.renderscript.Allocation
+Allocations} but no output {@link android.renderscript.Allocation Allocations}.</p></li>
+
+<p>Reduction kernels are explained in more detail <a href="#reduction-in-depth">here</a>.</p>
+
+<p>Reduction kernels are supported in Android Nougat (API level 24) and later.</p>
+</li>
+</ul>
+
+<p>A mapping kernel function or a reduction kernel accumulator function may access the coordinates
+of the current execution using the <a id="special-arguments">special arguments</a> <code>x</code>,
+<code>y</code>, and <code>z</code>, which must be of type <code>int</code> or <code>uint32_t</code>.
+These arguments are optional.</p>
+
+<p>A mapping kernel function or a reduction kernel accumulator
+function may also take the optional special argument
+<code>context</code> of type <a
+href='reference/rs_for_each.html#android_rs:rs_kernel_context'>rs_kernel_context</a>.
+It is needed by a family of runtime APIs that are used to query
+certain properties of the current execution -- for example, <a
+href='reference/rs_for_each.html#android_rs:rsGetDimX'>rsGetDimX</a>.
+(The <code>context</code> argument is available in Android 6.0 (API level 23) and later.)</p>
+</li>
 
 <li>An optional <code>init()</code> function. An <code>init()</code> function is a special type of
-invokable function that is run when the script is first instantiated. This allows for some
+invokable function that RenderScript runs when the script is first instantiated. This allows for some
 computation to occur automatically at script creation.</li>
 
-<li>Some number of static script globals and functions. A static script global is equivalent to a
-script global except that it cannot be set from Java code. A static function is a standard C
+<li>Zero or more <strong><i>static script globals and functions</i></strong>. A static script global is equivalent to a
+script global except that it cannot be accessed from Java code. A static function is a standard C
 function that can be called from any kernel or invokable function in the script but is not exposed
 to the Java API. If a script global or function does not need to be called from Java code, it is
-highly recommended that those be declared <code>static</code>.</li> </ul>
+highly recommended that it be declared <code>static</code>.</li> </ul>
 
 <h4>Setting floating point precision</h4>
 
@@ -129,13 +220,13 @@
 
 </li>
 
-  <li><code>#pragma rs_fp_relaxed</code> - For apps that don’t require strict IEEE 754-2008
+  <li><code>#pragma rs_fp_relaxed</code>: For apps that don’t require strict IEEE 754-2008
     compliance and can tolerate less precision. This mode enables flush-to-zero for denorms and
     round-towards-zero.
 
 </li>
 
-  <li><code>#pragma rs_fp_imprecise</code> - For apps that don’t have stringent precision
+  <li><code>#pragma rs_fp_imprecise</code>: For apps that don’t have stringent precision
     requirements. This mode enables everything in <code>rs_fp_relaxed</code> along with the
     following:
 
@@ -162,14 +253,21 @@
     available on devices running Android 3.0 (API level 11) and higher. </li>
   <li><strong>{@link android.support.v8.renderscript}</strong> - The APIs in this package are
     available through a <a href="{@docRoot}tools/support-library/features.html#v8">Support
-    Library</a>, which allows you to use them on devices running Android 2.2 (API level 8) and
+    Library</a>, which allows you to use them on devices running Android 2.3 (API level 9) and
     higher.</li>
 </ul>
 
-<p>We strongly recommend using the Support Library APIs for accessing RenderScript because they
-  provide a wider range of device compatibility. Developers targeting specific versions of
-  Android can use {@link android.renderscript} if necessary.</p>
+<p>Here are the tradeoffs:</p>
 
+<ul>
+<li>If you use the Support Library APIs, the RenderScript portion of your application will be
+  compatible with devices running Android 2.3 (API level 9) and higher, regardless of which RenderScript
+  features you use. This allows your application to work on more devices than if you use the
+  native (<strong>{@link android.renderscript}</strong>) APIs.</li>
+<li>Certain RenderScript features are not available through the Support Library APIs.</li>
+<li>If you use the Support Library APIs, you will get (possibly significantly) larger APKs than
+if you use the native (<strong>{@link android.renderscript}</strong>) APIs.</li>
+</ul>
 
 <h3 id="ide-setup">Using the RenderScript Support Library APIs</h3>
 
@@ -202,7 +300,7 @@
     buildToolsVersion "23.0.3"
 
     defaultConfig {
-        minSdkVersion 8
+        minSdkVersion 9
         targetSdkVersion 19
 <strong>
         renderscriptTargetApi 18
@@ -250,7 +348,7 @@
 
 <p>Using RenderScript from Java code relies on the API classes located in the
 {@link android.renderscript} or the {@link android.support.v8.renderscript} package. Most
-applications follow the same basic usage patterns:</p>
+applications follow the same basic usage pattern:</p>
 
 <ol>
 
@@ -266,12 +364,12 @@
 script.</strong> An {@link android.renderscript.Allocation} is a RenderScript object that provides
 storage for a fixed amount of data. Kernels in scripts take {@link android.renderscript.Allocation}
 objects as their input and output, and {@link android.renderscript.Allocation} objects can be
-accessed in kernels using <code>rsGetElementAt_<em>type</em>()</code> and
-<code>rsSetElementAt_<em>type</em>()</code> when bound as script globals. {@link
+accessed in kernels using <code>rsGetElementAt_<i>type</i>()</code> and
+<code>rsSetElementAt_<i>type</i>()</code> when bound as script globals. {@link
 android.renderscript.Allocation} objects allow arrays to be passed from Java code to RenderScript
 code and vice-versa. {@link android.renderscript.Allocation} objects are typically created using
-{@link android.renderscript.Allocation#createTyped} or {@link
-android.renderscript.Allocation#createFromBitmap}.</li>
+{@link android.renderscript.Allocation#createTyped createTyped()} or {@link
+android.renderscript.Allocation#createFromBitmap createFromBitmap()}.</li>
 
 <li><strong>Create whatever scripts are necessary.</strong> There are two types of scripts available
 to you when using RenderScript:
@@ -281,9 +379,9 @@
 <li><strong>ScriptC</strong>: These are the user-defined scripts as described in <a
 href="#writing-an-rs-kernel">Writing a RenderScript Kernel</a> above. Every script has a Java class
 reflected by the RenderScript compiler in order to make it easy to access the script from Java code;
-this class will have the name <code>ScriptC_<em>filename</em></code>. For example, if the kernel
-above was located in <code>invert.rs</code> and a RenderScript context was already located in
-<code>mRS</code>, the Java code to instantiate the script would be:
+this class has the name <code>ScriptC_<i>filename</i></code>. For example, if the mapping kernel
+above were located in <code>invert.rs</code> and a RenderScript context were already located in
+<code>mRenderScript</code>, the Java code to instantiate the script would be:
 
 <pre>ScriptC_invert invert = new ScriptC_invert(mRenderScript);</pre></li>
 
@@ -294,35 +392,926 @@
 </ul></li>
 
 <li><strong>Populate Allocations with data.</strong> Except for Allocations created with {@link
-android.renderscript#createFromBitmap}, an Allocation will be populated with empty data when it is
-first created. To populate an Allocation, use one of the <code>copy</code> methods in {@link
-android.renderscript.Allocation}.</li>
+android.renderscript.Allocation#createFromBitmap createFromBitmap()}, an Allocation is populated with empty data when it is
+first created. To populate an Allocation, use one of the "copy" methods in {@link
+android.renderscript.Allocation}. The "copy" methods are <a href="#asynchronous-model">synchronous</a>.</li>
 
-<li><strong>Set any necessary script globals.</strong> Globals may be set using methods in the same
-<code>ScriptC_<em>filename</em></code> class with methods named
-<code>set_<em>globalname</em></code>. For example, in order to set an <code>int</code> named
-<code>elements</code>, use the Java method <code>set_elements(int)</code>. RenderScript objects can
-also be set in kernels; for example, the <code>rs_allocation</code> variable named
-<code>lookup</code> can be set with the method <code>set_lookup(Allocation)</code>.</li>
+<li><strong>Set any necessary script globals.</strong> You may set globals using methods in the
+  same <code>ScriptC_<i>filename</i></code> class named <code>set_<i>globalname</i></code>. For
+  example, in order to set an <code>int</code> variable named <code>threshold</code>, use the
+  Java method <code>set_threshold(int)</code>; and in order to set
+  an <code>rs_allocation</code> variable named <code>lookup</code>, use the Java
+  method <code>set_lookup(Allocation)</code>. The <code>set</code> methods
+  are <a href="#asynchronous-model">asynchronous</a>.</li>
 
-<li><strong>Launch the appropriate kernels.</strong> Methods to launch a given kernel will be
-reflected in the same <code>ScriptC_<em>filename</em></code> class with methods named
-<code>forEach_<em>kernelname</em>()</code>. These launches are asynchronous, and launches will be
-serialized in the order in which they are launched. Depending on the arguments to the kernel, the
-method will take either one or two Allocations. By default, a kernel will execute over the entire
-input or output Allocation; to execute over a subset of that Allocation, pass an appropriate {@link
-android.renderscript.Script.LaunchOptions} as the last argument to the <code>forEach</code> method.
+<li><strong>Launch the appropriate kernels and invokable functions.</strong>
+<p>Methods to launch a given kernel are
+reflected in the same <code>ScriptC_<i>filename</i></code> class with methods named
+<code>forEach_<i>mappingKernelName</i>()</code>
+or <code>reduce_<i>reductionKernelName</i>()</code>.
+These launches are <a href="#asynchronous-model">asynchronous</a>.
+Depending on the arguments to the kernel, the
+method takes one or more Allocations, all of which must have the same dimensions. By default, a
+kernel executes over every coordinate in those dimensions; to execute a kernel over a subset of those coordinates,
+pass an appropriate {@link
+android.renderscript.Script.LaunchOptions} as the last argument to the <code>forEach</code> or <code>reduce</code> method.</p>
 
-<p>Invoked functions can be launched using the <code>invoke_<em>functionname</em></code> methods
-reflected in the same <code>ScriptC_<em>filename</em></code> class.</p></li>
+<p>Launch invokable functions using the <code>invoke_<i>functionName</i></code> methods
+reflected in the same <code>ScriptC_<i>filename</i></code> class.
+These launches are <a href="#asynchronous-model">asynchronous</a>.</p></li>
 
-<li><strong>Copy data out of {@link android.renderscript.Allocation} objects.</strong> In order to
-access data from an {@link android.renderscript.Allocation} from Java code, that data must be copied
-back to Java buffers using one of the <code>copy</code> methods in {@link
-android.renderscript.Allocation}. These functions will synchronize with asynchronous kernel and
-function launches as necessary.</li>
+<li><strong>Retrieve data from {@link android.renderscript.Allocation} objects
+and <i><a href="#javaFutureType">javaFutureType</a></i> objects.</strong>
+In order to
+access data from an {@link android.renderscript.Allocation} from Java code, you must copy that data
+back to Java using one of the "copy" methods in {@link
+android.renderscript.Allocation}.
+In order to obtain the result of a reduction kernel, you must use the <code><i>javaFutureType</i>.get()</code> method.
+The "copy" and <code>get()</code> methods are <a href="#asynchronous-model">synchronous</a>.</li>
 
-<li><strong>Tear down the RenderScript context.</strong> The RenderScript context can be destroyed
+<li><strong>Tear down the RenderScript context.</strong> You can destroy the RenderScript context
 with {@link android.renderscript.RenderScript#destroy} or by allowing the RenderScript context
-object to be garbage collected. This will cause any further use of any object belonging to that
+object to be garbage collected. This causes any further use of any object belonging to that
 context to throw an exception.</li> </ol>
+
+<h3 id="asynchronous-model">Asynchronous execution model</h3>
+
+<p>The reflected <code>forEach</code>, <code>invoke</code>, <code>reduce</code>,
+  and <code>set</code> methods are asynchronous -- each may return to Java before completing the
+  requested action.  However, the individual actions are serialized in the order in which they are launched.</p>
+
+<p>The {@link android.renderscript.Allocation} class provides "copy" methods to copy data to
+  and from Allocations.  A "copy" method is synchronous, and is serialized with respect to any
+  of the asynchronous actions above that touch the same Allocation.</p>
+
+<p>The reflected <i><a href="#javaFutureType">javaFutureType</a></i> classes provide
+  a <code>get()</code> method to obtain the result of a reduction. <code>get()</code> is
+  synchronous, and is serialized with respect to the reduction (which is asynchronous).</p>
+
+<h2 id="reduction-in-depth">Reduction Kernels in Depth</h2>
+
+<p><i>Reduction</i> is the process of combining a collection of data into a single
+value. This is a useful primitive in parallel programming, with applications such as the
+following:</p>
+<ul>
+  <li>computing the sum or product over all the data</li>
+  <li>computing logical operations (<code>and</code>, <code>or</code>, <code>xor</code>)
+  over all the data</li>
+  <li>finding the minimum or maximum value within the data</li>
+  <li>searching for a specific value or for the coordinate of a specific value within the data</li>
+</ul>
+
+<p>In Android Nougat (API level 24) and later, RenderScript supports <i>reduction kernels</i> to allow
+efficient user-written reduction algorithms. You may launch reduction kernels on inputs with
+1, 2, or 3 dimensions.<p>
+
+<p>An example above shows a simple <a href="#example-addint">addint</a> reduction kernel.
+Here is a more complicated <a id="example-findMinAndMax">findMinAndMax</a> reduction kernel
+that finds the locations of the minimum and maximum <code>long</code> values in a
+1-dimensional {@link android.renderscript.Allocation}:</p>
+
+<pre>
+#define LONG_MAX (long)((1UL << 63) - 1)
+#define LONG_MIN (long)(1UL << 63)
+
+#pragma rs reduce(findMinAndMax) \
+  initializer(fMMInit) accumulator(fMMAccumulator) \
+  combiner(fMMCombiner) outconverter(fMMOutConverter)
+
+// Either a value and the location where it was found, or <a href="#INITVAL">INITVAL</a>.
+typedef struct {
+  long val;
+  int idx;     // -1 indicates <a href="#INITVAL">INITVAL</a>
+} IndexedVal;
+
+typedef struct {
+  IndexedVal min, max;
+} MinAndMax;
+
+// In discussion below, this initial value { { LONG_MAX, -1 }, { LONG_MIN, -1 } }
+// is called <a id="INITVAL">INITVAL</a>.
+static void fMMInit(MinAndMax *accum) {
+  accum->min.val = LONG_MAX;
+  accum->min.idx = -1;
+  accum->max.val = LONG_MIN;
+  accum->max.idx = -1;
+}
+
+//----------------------------------------------------------------------
+// In describing the behavior of the accumulator and combiner functions,
+// it is helpful to describe hypothetical functions
+//   IndexedVal min(IndexedVal a, IndexedVal b)
+//   IndexedVal max(IndexedVal a, IndexedVal b)
+//   MinAndMax  minmax(MinAndMax a, MinAndMax b)
+//   MinAndMax  minmax(MinAndMax accum, IndexedVal val)
+//
+// The effect of
+//   IndexedVal min(IndexedVal a, IndexedVal b)
+// is to return the IndexedVal from among the two arguments
+// whose val is lesser, except that when an IndexedVal
+// has a negative index, that IndexedVal is never less than
+// any other IndexedVal; therefore, if exactly one of the
+// two arguments has a negative index, the min is the other
+// argument. Like ordinary arithmetic min and max, this function
+// is commutative and associative; that is,
+//
+//   min(A, B) == min(B, A)               // commutative
+//   min(A, min(B, C)) == min((A, B), C)  // associative
+//
+// The effect of
+//   IndexedVal max(IndexedVal a, IndexedVal b)
+// is analogous (greater . . . never greater than).
+//
+// Then there is
+//
+//   MinAndMax minmax(MinAndMax a, MinAndMax b) {
+//     return MinAndMax(min(a.min, b.min), max(a.max, b.max));
+//   }
+//
+// Like ordinary arithmetic min and max, the above function
+// is commutative and associative; that is:
+//
+//   minmax(A, B) == minmax(B, A)                  // commutative
+//   minmax(A, minmax(B, C)) == minmax((A, B), C)  // associative
+//
+// Finally define
+//
+//   MinAndMax minmax(MinAndMax accum, IndexedVal val) {
+//     return minmax(accum, MinAndMax(val, val));
+//   }
+//----------------------------------------------------------------------
+
+// This function can be explained as doing:
+//   *accum = minmax(*accum, IndexedVal(in, x))
+//
+// This function simply computes minimum and maximum values as if
+// INITVAL.min were greater than any other minimum value and
+// INITVAL.max were less than any other maximum value.  Note that if
+// *accum is INITVAL, then this function sets
+//   *accum = IndexedVal(in, x)
+//
+// After this function is called, both accum->min.idx and accum->max.idx
+// will have nonnegative values:
+// - x is always nonnegative, so if this function ever sets one of the
+//   idx fields, it will set it to a nonnegative value
+// - if one of the idx fields is negative, then the corresponding
+//   val field must be LONG_MAX or LONG_MIN, so the function will always
+//   set both the val and idx fields
+static void fMMAccumulator(MinAndMax *accum, long in, int x) {
+  IndexedVal me;
+  me.val = in;
+  me.idx = x;
+
+  if (me.val <= accum->min.val)
+    accum->min = me;
+  if (me.val >= accum->max.val)
+    accum->max = me;
+}
+
+// This function can be explained as doing:
+//   *accum = minmax(*accum, *val)
+//
+// This function simply computes minimum and maximum values as if
+// INITVAL.min were greater than any other minimum value and
+// INITVAL.max were less than any other maximum value.  Note that if
+// one of the two accumulator data items is INITVAL, then this
+// function sets *accum to the other one.
+static void fMMCombiner(MinAndMax *accum,
+                        const MinAndMax *val) {
+  if ((accum->min.idx < 0) || (val->min.val < accum->min.val))
+    accum->min = val->min;
+  if ((accum->max.idx < 0) || (val->max.val > accum->max.val))
+    accum->max = val->max;
+}
+
+static void fMMOutConverter(int2 *result,
+                            const MinAndMax *val) {
+  result->x = val->min.idx;
+  result->y = val->max.idx;
+}
+</pre>
+
+<p class="note"><strong>NOTE:</strong> There are more example reduction
+  kernels <a href="#more-example">here</a>.</p>
+
+<p>In order to run a reduction kernel, the RenderScript runtime creates <em>one or more</em>
+variables called <a id="accumulator-data-items"><strong><i>accumulator data
+items</i></strong></a> to hold the state of the reduction process. The RenderScript runtime
+picks the number of accumulator data items in such a way as to maximize performance. The type
+of the accumulator data items (<i>accumType</i>) is determined by the kernel's <i>accumulator
+function</i> -- the first argument to that function is a pointer to an accumulator data
+item. By default, every accumulator data item is initialized to zero (as if
+by <code>memset</code>); however, you may write an <i>initializer function</i> to do something
+different.</p>
+
+<p class="note"><strong>Example:</strong> In the <a href="#example-addint">addint</a>
+kernel, the accumulator data items (of type <code>int</code>) are used to add up input
+values. There is no initializer function, so each accumulator data item is initialized to
+zero.</p>
+
+<p class="note"><strong>Example:</strong> In
+the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, the accumulator data items
+(of type <code>MinAndMax</code>) are used to keep track of the minimum and maximum values
+found so far. There is an initializer function to set these to <code>LONG_MAX</code> and
+<code>LONG_MIN</code>, respectively; and to set the locations of these values to -1, indicating that
+the values are not actually present in the (empty) portion of the input that has been
+processed.</p>
+
+<p>RenderScript calls your accumulator function once for every coordinate in the
+input(s). Typically, your function should update the accumulator data item in some way
+according to the input.</p>
+
+<p class="note"><strong>Example:</strong> In the <a href="#example-addint">addint</a>
+kernel, the accumulator function adds the value of an input Element to the accumulator
+data item.</p>
+
+<p class="note"><strong>Example:</strong> In
+the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, the accumulator function
+checks to see whether the value of an input Element is less than or equal to the minimum
+value recorded in the accumulator data item and/or greater than or equal to the maximum
+value recorded in the accumulator data item, and updates the accumulator data item
+accordingly.</p>
+
+<p>After the accumulator function has been called once for every coordinate in the input(s),
+RenderScript must <strong>combine</strong> the <a href="#accumulator-data-items">accumulator
+data items</a> together into a single accumulator data item. You may write a <i>combiner
+function</i> to do this. If the accumulator function has a single input and
+no <a href="#special-arguments">special arguments</a>, then you do not need to write a combiner
+function; RenderScript will use the accumulator function to combine the accumulator data
+items. (You may still write a combiner function if this default behavior is not what you
+want.)</p>
+
+<p class="note"><strong>Example:</strong> In the <a href="#example-addint">addint</a>
+kernel, there is no combiner function, so the accumulator function will be used. This is
+the correct behavior, because if we split a collection of values into two pieces, and we
+add up the values in those two pieces separately, adding up those two sums is the same as
+adding up the entire collection.</p>
+
+<p class="note"><strong>Example:</strong> In
+the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, the combiner function
+checks to see whether the minimum value recorded in the "source" accumulator data
+item <code>*val</code> is less then the minimum value recorded in the "destination"
+accumulator data item <code>*accum</code>, and updates <code>*accum</code>
+accordingly. It does similar work for the maximum value. This updates <code>*accum</code>
+to the state it would have had if all of the input values had been accumulated into
+<code>*accum</code> rather than some into <code>*accum</code> and some into
+<code>*val</code>.</p>
+
+<p>After all of the accumulator data items have been combined, RenderScript determines
+the result of the reduction to return to Java. You may write an <i>outconverter
+function</i> to do this. You do not need to write an outconverter function if you want
+the final value of the combined accumulator data items to be the result of the reduction.</p>
+
+<p class="note"><strong>Example:</strong> In the <a href="#example-addint">addint</a> kernel,
+there is no outconverter function.  The final value of the combined data items is the sum of
+all Elements of the input, which is the value we want to return.</p>
+
+<p class="note"><strong>Example:</strong> In
+the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, the outconverter function
+initializes an <code>int2</code> result value to hold the locations of the minimum and
+maximum values resulting from the combination of all of the accumulator data items.</p>
+
+<h3 id="writing-reduction-kernel">Writing a reduction kernel</h3>
+
+<p><code>#pragma rs reduce</code> defines a reduction kernel by
+specifying its name and the names and roles of the functions that make
+up the kernel.  All such functions must be
+<code>static</code>. A reduction kernel always requires an <code>accumulator</code>
+function; you can omit some or all of the other functions, depending on what you want the
+kernel to do.</p>
+
+<pre>#pragma rs reduce(<i>kernelName</i>) \
+  initializer(<i>initializerName</i>) \
+  accumulator(<i>accumulatorName</i>) \
+  combiner(<i>combinerName</i>) \
+  outconverter(<i>outconverterName</i>)
+</pre>
+
+<p>The meaning of the items in the <code>#pragma</code> is as follows:</p>
+<ul>
+
+<li><code>reduce(<i>kernelName</i>)</code> (mandatory): Specifies that a reduction kernel is
+being defined. A reflected Java method <code>reduce_<i>kernelName</i></code> will launch the
+kernel.</li>
+
+<li><p><code>initializer(<i>initializerName</i>)</code> (optional): Specifies the name of the
+initializer function for this reduction kernel. When you launch the kernel, RenderScript calls
+this function once for each <a href="#accumulator-data-items">accumulator data item</a>. The
+function must be defined like this:</p>
+
+<pre>static void <i>initializerName</i>(<i>accumType</i> *accum) { … }</pre>
+
+<p><code>accum</code> is a pointer to an accumulator data item for this function to
+initialize.</p>
+
+<p>If you do not provide an initializer function, RenderScript initializes every accumulator
+data item to zero (as if by <code>memset</code>), behaving as if there were an initializer
+function that looks like this:</p>
+<pre>static void <i>initializerName</i>(<i>accumType</i> *accum) {
+  memset(accum, 0, sizeof(*accum));
+}</pre>
+</li>
+
+<li><p><code><a id="accumulator-function">accumulator(<i>accumulatorName</i>)</a></code>
+(mandatory): Specifies the name of the accumulator function for this
+reduction kernel. When you launch the kernel, RenderScript calls
+this function once for every coordinate in the input(s), to update an
+accumulator data item in some way according to the input(s). The function
+must be defined like this:</p>
+
+<pre>
+static void <i>accumulatorName</i>(<i>accumType</i> *accum,
+                            <i>in1Type</i> in1, <i>&hellip;,</i> <i>inNType</i> in<i>N</i>
+                            <i>[, specialArguments]</i>) { &hellip; }
+</pre>
+
+<p><code>accum</code> is a pointer to an accumulator data item for this function to
+modify. <code>in1</code> through <code>in<i>N</i></code> are one <em>or more</em> arguments that
+are automatically filled in based on the inputs passed to the kernel launch, one argument
+per input. The accumulator function may optionally take any of the <a
+href="#special-arguments">special arguments</a>.</p>
+
+<p>An example kernel with multiple inputs is <a href="#dot-product"><code>dotProduct</code></a>.</p>
+</li>
+
+<li><code><a id="combiner-function">combiner(<i>combinerName</i>)</a></code>
+(optional): Specifies the name of the combiner function for this
+reduction kernel. After RenderScript calls the accumulator function
+once for every coordinate in the input(s), it calls this function as many
+times as necessary to combine all accumulator data items into a single
+accumulator data item. The function must be defined like this:</p>
+
+<pre>static void <i>combinerName</i>(<i>accumType</i> *accum, const <i>accumType</i> *other) { … }</pre>
+
+<p><code>accum</code> is a pointer to a "destination" accumulator data item for this
+function to modify. <code>other</code> is a pointer to a "source" accumulator data item
+for this function to "combine" into <code>*accum</code>.</p>
+
+<p class="note"><strong>NOTE:</strong> It is possible
+  that <code>*accum</code>, <code>*other</code>, or both have been initialized but have never
+  been passed to the accumulator function; that is, one or both have never been updated
+  according to any input data. For example, in
+  the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, the combiner
+  function <code>fMMCombiner</code> explicitly checks for <code>idx &lt; 0</code> because that
+  indicates such an accumulator data item, whose value is <a href="#INITVAL">INITVAL</a>.</p>
+
+<p>If you do not provide a combiner function, RenderScript uses the accumulator function in its
+place, behaving as if there were a combiner function that looks like this:</p>
+
+<pre>static void <i>combinerName</i>(<i>accumType</i> *accum, const <i>accumType</i> *other) {
+  <i>accumulatorName</i>(accum, *other);
+}</pre>
+
+<p>A combiner function is mandatory if the kernel has more than one input, if the input data
+  type is not the same as the accumulator data type, or if the accumulator function takes one
+  or more <a href="#special-arguments">special arguments</a>.</p>
+</li>
+
+<li><p><code><a id="outconverter-function">outconverter(<i>outconverterName</i>)</a></code>
+(optional): Specifies the name of the outconverter function for this
+reduction kernel. After RenderScript combines all of the accumulator
+data items, it calls this function to determine the result of the
+reduction to return to Java. The function must be defined like
+this:</p>
+
+<pre>static void <i>outconverterName</i>(<i>resultType</i> *result, const <i>accumType</i> *accum) { … }</pre>
+
+<p><code>result</code> is a pointer to a result data item (allocated but not initialized
+by the RenderScript runtime) for this function to initialize with the result of the
+reduction. <i>resultType</i> is the type of that data item, which need not be the same
+as <i>accumType</i>. <code>accum</code> is a pointer to the final accumulator data item
+computed by the <a href="#combiner-function">combiner function</a>.</p>
+
+<p>If you do not provide an outconverter function, RenderScript copies the final accumulator
+data item to the result data item, behaving as if there were an outconverter function that
+looks like this:</p>
+
+<pre>static void <i>outconverterName</i>(<i>accumType</i> *result, const <i>accumType</i> *accum) {
+  *result = *accum;
+}</pre>
+
+<p>If you want a different result type than the accumulator data type, then the outconverter function is mandatory.</p>
+</li>
+
+</ul>
+
+<p>Note that a kernel has input types, an accumulator data item type, and a result type,
+  none of which need to be the same. For example, in
+  the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, the input
+  type <code>long</code>, accumulator data item type <code>MinAndMax</code>, and result
+  type <code>int2</code> are all different.</p>
+
+<h4 id="assume">What can't you assume?</h4>
+
+<p>You must not rely on the number of accumulator data items created by RenderScript for a
+  given kernel launch.  There is no guarantee that two launches of the same kernel with the
+  same input(s) will create the same number of accumulator data items.</p>
+
+<p>You must not rely on the order in which RenderScript calls the initializer, accumulator, and
+  combiner functions; it may even call some of them in parallel.  There is no guarantee that
+  two launches of the same kernel with the same input will follow the same order.  The only
+  guarantee is that only the initializer function will ever see an uninitialized accumulator
+  data item. For example:</p>
+<ul>
+<li>There is no guarantee that all accumulator data items will be initialized before the
+  accumulator function is called, although it will only be called on an initialized accumulator
+  data item.</li>
+<li>There is no guarantee on the order in which input Elements are passed to the accumulator
+  function.</li>
+<li>There is no guarantee that the accumulator function has been called for all input Elements
+  before the combiner function is called.</li>
+</ul>
+
+<p>One consequence of this is that the <a href="#example-findMinAndMax">findMinAndMax</a>
+  kernel is not deterministic: If the input contains more than one occurrence of the same
+  minimum or maximum value, you have no way of knowing which occurrence the kernel will
+  find.</p>
+
+<h4 id="guarantee">What must you guarantee?</h4>
+
+<p>Because the RenderScript system can choose to execute a kernel <a href="#assume">in many
+    different ways</a>, you must follow certain rules to ensure that your kernel behaves the
+    way you want. If you do not follow these rules, you may get incorrect results,
+    nondeterministic behavior, or runtime errors.</p>
+
+<p>The rules below often say that two accumulator data items must have "<a id="the-same">the
+  same value"</a>.  What does this mean?  That depends on what you want the kernel to do.  For
+  a mathematical reduction such as <a href="#example-addint">addint</a>, it usually makes sense
+  for "the same" to mean mathematical equality.  For a "pick any" search such
+  as <a href="#example-findMinAndMax">findMinAndMax</a> ("find the location of minimum and
+  maximum input values") where there might be more than one occurrence of identical input
+  values, all locations of a given input value must be considered "the same".  You could write
+  a similar kernel to "find the location of <em>leftmost</em> minimum and maximum input values"
+  where (say) a minimum value at location 100 is preferred over an identical minimum value at location
+  200; for this kernel, "the same" would mean identical <em>location</em>, not merely
+  identical <em>value</em>, and the accumulator and combiner functions would have to be
+  different than those for <a href="#example-findMinAndMax">findMinAndMax</a>.</p>
+
+<strong>The initializer function must create an <i>identity value</i>.</strong>  That is,
+  if <code><i>I</i></code> and <code><i>A</i></code> are accumulator data items initialized
+  by the initializer function, and <code><i>I</i></code> has never been passed to the
+  accumulator function (but <code><i>A</i></code> may have been), then
+<ul>
+<li><code><i>combinerName</i>(&<i>A</i>, &<i>I</i>)</code> must
+  leave <code><i>A</i></code> <a href="#the-same">the same</a></li>
+<li><code><i>combinerName</i>(&<i>I</i>, &<i>A</i>)</code> must
+  leave <code><i>I</i></code> <a href="#the-same">the same</a> as <code><i>A</i></code></li>
+</ul>
+<p class="note"><strong>Example:</strong> In the <a href="#example-addint">addint</a>
+  kernel, an accumulator data item is initialized to zero. The combiner function for this
+  kernel performs addition; zero is the identity value for addition.</p>
+<div class="note">
+<p><strong>Example:</strong> In the <a href="#example-findMinAndMax">findMinAndMax</a>
+  kernel, an accumulator data item is initialized
+  to <a href="#INITVAL"><code>INITVAL</code></a>.
+<ul>
+<li><code>fMMCombiner(&<i>A</i>, &<i>I</i>)</code> leaves <code><i>A</i></code> the same,
+  because <code><i>I</i></code> is <code>INITVAL</code>.</li>
+<li><code>fMMCombiner(&<i>I</i>, &<i>A</i>)</code> sets <code><i>I</i></code>
+  to <code><i>A</i></code>, because <code><i>I</i></code> is <code>INITVAL</code>.</li>
+</ul>
+Therefore, <code>INITVAL</code> is indeed an identity value.
+</p></div>
+
+<p><strong>The combiner function must be <i>commutative</i>.</strong>  That is,
+  if <code><i>A</i></code> and <code><i>B</i></code> are accumulator data items initialized
+  by the initializer function, and that may have been passed to the accumulator function zero
+  or more times, then <code><i>combinerName</i>(&<i>A</i>, &<i>B</i>)</code> must
+  set <code><i>A</i></code> to <a href="#the-same">the same value</a>
+  that <code><i>combinerName</i>(&<i>B</i>, &<i>A</i>)</code>
+  sets <code><i>B</i></code>.</p>
+<p class="note"><strong>Example:</strong> In the <a href="#example-addint">addint</a>
+  kernel, the combiner function adds the two accumulator data item values; addition is
+  commutative.</p>
+<div class="note">
+<p><strong>Example:</strong> In the <a href="#example-findMinAndMax">findMinAndMax</a> kernel,
+<pre>
+fMMCombiner(&<i>A</i>, &<i>B</i>)
+</pre>
+is the same as
+<pre>
+<i>A</i> = minmax(<i>A</i>, <i>B</i>)
+</pre>
+and <code>minmax</code> is commutative, so <code>fMMCombiner</code> is also.
+</p>
+</div>
+
+<p><strong>The combiner function must be <i>associative</i>.</strong>  That is,
+  if <code><i>A</i></code>, <code><i>B</i></code>, and <code><i>C</i></code> are
+  accumulator data items initialized by the initializer function, and that may have been passed
+  to the accumulator function zero or more times, then the following two code sequences must
+  set <code><i>A</i></code> to <a href="#the-same">the same value</a>:</p>
+<ul>
+<li><pre>
+<i>combinerName</i>(&<i>A</i>, &<i>B</i>);
+<i>combinerName</i>(&<i>A</i>, &<i>C</i>);
+</pre></li>
+<li><pre>
+<i>combinerName</i>(&<i>B</i>, &<i>C</i>);
+<i>combinerName</i>(&<i>A</i>, &<i>B</i>);
+</pre></li>
+</ul>
+<div class="note">
+<p><strong>Example:</strong> In the <a href="#example-addint">addint</a> kernel, the
+  combiner function adds the two accumulator data item values:
+<ul>
+<li><pre>
+<i>A</i> = <i>A</i> + <i>B</i>
+<i>A</i> = <i>A</i> + <i>C</i>
+// Same as
+//   <i>A</i> = (<i>A</i> + <i>B</i>) + <i>C</i>
+</pre></li>
+<li><pre>
+<i>B</i> = <i>B</i> + <i>C</i>
+<i>A</i> = <i>A</i> + <i>B</i>
+// Same as
+//   <i>A</i> = <i>A</i> + (<i>B</i> + <i>C</i>)
+//   <i>B</i> = <i>B</i> + <i>C</i>
+</li>
+</ul>
+Addition is associative, and so the combiner function is also.
+</p>
+</div>
+<div class="note">
+<p><strong>Example:</strong> In the <a href="#example-findMinAndMax">findMinAndMax</a> kernel,
+<pre>
+fMMCombiner(&<i>A</i>, &<i>B</i>)
+</pre>
+is the same as
+<pre>
+<i>A</i> = minmax(<i>A</i>, <i>B</i>)
+</pre>
+So the two sequences are
+<ul>
+<li><pre>
+<i>A</i> = minmax(<i>A</i>, <i>B</i>)
+<i>A</i> = minmax(<i>A</i>, <i>C</i>)
+// Same as
+//   <i>A</i> = minmax(minmax(<i>A</i>, <i>B</i>), <i>C</i>)
+</pre></li>
+<li><pre>
+<i>B</i> = minmax(<i>B</i>, <i>C</i>)
+<i>A</i> = minmax(<i>A</i>, <i>B</i>)
+// Same as
+//   <i>A</i> = minmax(<i>A</i>, minmax(<i>B</i>, <i>C</i>))
+//   <i>B</i> = minmax(<i>B</i>, <i>C</i>)
+</pre></li>
+<code>minmax</code> is associative, and so <code>fMMCombiner</code> is also.
+</p>
+</div>
+
+<p><strong>The accumulator function and combiner function together must obey the <i>basic
+  folding rule</i>.</strong>  That is, if <code><i>A</i></code>
+  and <code><i>B</i></code> are accumulator data items, <code><i>A</i></code> has been
+  initialized by the initializer function and may have been passed to the accumulator function
+  zero or more times, <code><i>B</i></code> has not been initialized, and <i>args</i> is
+  the list of input arguments and special arguments for a particular call to the accumulator
+  function, then the following two code sequences must set <code><i>A</i></code>
+  to <a href="#the-same">the same value</a>:</p>
+<ul>
+<li><pre>
+<i>accumulatorName</i>(&<i>A</i>, <i>args</i>);  // statement 1
+</pre></li>
+<li><pre>
+<i>initializerName</i>(&<i>B</i>);        // statement 2
+<i>accumulatorName</i>(&<i>B</i>, <i>args</i>);  // statement 3
+<i>combinerName</i>(&<i>A</i>, &<i>B</i>);       // statement 4
+</pre></li>
+</ul>
+<div class="note">
+<p><strong>Example:</strong> In the <a href="#example-addint">addint</a> kernel, for an input value <i>V</i>:
+<ul>
+<li>Statement 1 is the same as <code>A += <i>V</i></code></li>
+<li>Statement 2 is the same as <code>B = 0</code></li>
+<li>Statement 3 is the same as <code>B += <i>V</i></code>, which is the same as <code>B = <i>V</i></code></li>
+<li>Statement 4 is the same as <code>A += B</code>, which is the same as <code>A += <i>V</i></code></li>
+</ul>
+Statements 1 and 4 set <code><i>A</i></code> to the same value, and so this kernel obeys the
+basic folding rule.
+</p>
+</div>
+<div class="note">
+<p><strong>Example:</strong> In the <a href="#example-findMinAndMax">findMinAndMax</a> kernel, for an input
+  value <i>V</i> at coordinate <i>X</i>:
+<ul>
+<li>Statement 1 is the same as <code>A = minmax(A, IndexedVal(<i>V</i>, <i>X</i>))</code></li>
+<li>Statement 2 is the same as <code>B = <a href="#INITVAL">INITVAL</a></code></li>
+<li>Statement 3 is the same as
+<pre>
+B = minmax(B, IndexedVal(<i>V</i>, <i>X</i>))
+</pre>
+which, because <i>B</i> is the initial value, is the same as
+<pre>
+B = IndexedVal(<i>V</i>, <i>X</i>)
+</pre>
+</li>
+<li>Statement 4 is the same as
+<pre>
+A = minmax(A, B)
+</pre>
+which is the same as
+<pre>
+A = minmax(A, IndexedVal(<i>V</i>, <i>X</i>))
+</pre>
+</ul>
+Statements 1 and 4 set <code><i>A</i></code> to the same value, and so this kernel obeys the
+basic folding rule.
+</p>
+</div>
+
+<h3 id="calling-reduction-kernel">Calling a reduction kernel from Java code</h3>
+
+<p>For a reduction kernel named <i>kernelName</i> defined in the
+file <code><i>filename</i>.rs</code>, there are three methods reflected in the
+class <code>ScriptC_<i>filename</i></code>:</p>
+
+<pre>
+// Method 1
+public <i>javaFutureType</i> reduce_<i>kernelName</i>(Allocation ain1, <i>&hellip;,</i>
+                                        Allocation ain<i>N</i>);
+
+// Method 2
+public <i>javaFutureType</i> reduce_<i>kernelName</i>(Allocation ain1, <i>&hellip;,</i>
+                                        Allocation ain<i>N</i>,
+                                        Script.LaunchOptions sc);
+
+// Method 3
+public <i>javaFutureType</i> reduce_<i>kernelName</i>(<i><a href="#devec">devecSiIn1Type</a></i>[] in1, &hellip;,
+                                        <i><a href="#devec">devecSiInNType</a></i>[] in<i>N</i>);
+</pre>
+
+<p>Here are some examples of calling the <a href="#example-addint">addint</a> kernel:</p>
+<pre>
+ScriptC_example script = new ScriptC_example(mRenderScript);
+
+// 1D array
+//   and obtain answer immediately
+int input1[] = <i>&hellip;</i>;
+int sum1 = script.reduce_addint(input1).get();  // Method 3
+
+// 2D allocation
+//   and do some additional work before obtaining answer
+Type.Builder typeBuilder =
+  new Type.Builder(RS, Element.I32(RS));
+typeBuilder.setX(<i>&hellip;</i>);
+typeBuilder.setY(<i>&hellip;</i>);
+Allocation input2 = createTyped(RS, typeBuilder.create());
+<i>populateSomehow</i>(input2);  // fill in input Allocation with data
+script.result_int result2 = script.reduce_addint(input2);  // Method 1
+<i>doSomeAdditionalWork</i>(); // might run at same time as reduction
+int sum2 = result2.get();
+</pre>
+
+<p><strong>Method 1</strong> has one input {@link android.renderscript.Allocation} argument for
+  every input argument in the kernel's <a href="#accumulator-function">accumulator
+    function</a>. The RenderScript runtime checks to ensure that all of the input Allocations
+  have the same dimensions and that the {@link android.renderscript.Element} type of each of
+  the input Allocations matches that of the corresponding input argument of the accumulator
+  function's prototype. If any of these checks fail, RenderScript throws an exception. The
+  kernel executes over every coordinate in those dimensions.</p>
+
+<p><strong>Method 2</strong> is the same as Method 1 except that Method 2 takes an additional
+  argument <code>sc</code> that can be used to limit the kernel execution to a subset of the
+  coordinates.</p>
+
+<p><strong><a id="reduce-method-3">Method 3</a></strong> is the same as Method 1 except that
+  instead of taking Allocation inputs it takes Java array inputs. This is a convenience that
+  saves you from having to write code to explicitly create an Allocation and copy data to it
+  from a Java array. <em>However, using Method 3 instead of Method 1 does not increase the
+  performance of the code</em>. For each input array, Method 3 creates a temporary
+  1-dimensional Allocation with the appropriate {@link android.renderscript.Element} type and
+  {@link android.renderscript.Allocation#setAutoPadding} enabled, and copies the array to the
+  Allocation as if by the appropriate <code>copyFrom()</code> method of {@link
+  android.renderscript.Allocation}. It then calls Method 1, passing those temporary
+  Allocations.</p>
+<p class="note"><strong>NOTE:</strong> If your application will make multiple kernel calls with
+  the same array, or with different arrays of the same dimensions and Element type, you may improve
+  performance by explicitly creating, populating, and reusing Allocations yourself, instead of
+  by using Method 3.</p>
+<p><strong><i><a id="javaFutureType">javaFutureType</a></i></strong>,
+  the return type of the reflected reduction methods, is a reflected
+  static nested class within the <code>ScriptC_<i>filename</i></code>
+  class. It represents the future result of a reduction
+  kernel run. To obtain the actual result of the run, call
+  the <code>get()</code> method of that class, which returns a value
+  of type <i>javaResultType</i>. <code>get()</code> is <a href="#asynchronous-model">synchronous</a>.</p>
+
+<pre>
+public class ScriptC_<i>filename</i> extends ScriptC {
+  public static class <i>javaFutureType</i> {
+    public <i>javaResultType</i> get() { &hellip; }
+  }
+}
+</pre>
+
+<p><strong><i>javaResultType</i></strong> is determined from the <i>resultType</i> of the
+  <a href="#outconverter-function">outconverter function</a>. Unless <i>resultType</i> is an
+  unsigned type (scalar, vector, or array), <i>javaResultType</i> is the directly corresponding
+  Java type. If <i>resultType</i> is an unsigned type and there is a larger Java signed type,
+  then <i>javaResultType</i> is that larger Java signed type; otherwise, it is the directly
+  corresponding Java type. For example:</p>
+<ul>
+<li>If <i>resultType</i> is <code>int</code>, <code>int2</code>, or <code>int[15]</code>,
+  then <i>javaResultType</i> is <code>int</code>, <code>Int2</code>,
+  or <code>int[]</code>. All values of <i>resultType</i> can be represented
+  by <i>javaResultType</i>.</li>
+<li>If <i>resultType</i> is <code>uint</code>, <code>uint2</code>, or <code>uint[15]</code>,
+  then <i>javaResultType</i> is <code>long</code>, <code>Long2</code>,
+  or <code>long[]</code>.  All values of <i>resultType</i> can be represented
+  by <i>javaResultType</i>.</li>
+<li>If <i>resultType</i> is <code>ulong</code>, <code>ulong2</code>,
+  or <code>ulong[15]</code>, then <i>javaResultType</i>
+  is <code>long</code>, <code>Long2</code>, or <code>long[]</code>. There are certain values
+  of <i>resultType</i> that cannot be represented by <i>javaResultType</i>.</li>
+</ul>
+
+<p><strong><i>javaFutureType</i></strong> is the future result type corresponding
+  to the <i>resultType</i> of the <a href="#outconverter-function">outconverter
+  function</a>.</p>
+<ul>
+<li>If <i>resultType</i> is not an array type, then <i>javaFutureType</i>
+  is <code>result_<i>resultType</i></code>.</li>
+<li>If <i>resultType</i> is an array of length <i>Count</i> with members of type <i>memberType</i>,
+  then <i>javaFutureType</i> is <code>resultArray<i>Count</i>_<i>memberType</i></code>.</li>
+</ul>
+
+<p>For example:</p>
+
+<pre>
+public class ScriptC_<i>filename</i> extends ScriptC {
+  // for kernels with int result
+  public static class result_int {
+    public int get() { &hellip; }
+  }
+
+  // for kernels with int[10] result
+  public static class resultArray10_int {
+    public int[] get() { &hellip; }
+  }
+
+  // for kernels with int2 result
+  //   note that the Java type name "Int2" is not the same as the script type name "int2"
+  public static class result_int2 {
+    public Int2 get() { &hellip; }
+  }
+
+  // for kernels with int2[10] result
+  //   note that the Java type name "Int2" is not the same as the script type name "int2"
+  public static class resultArray10_int2 {
+    public Int2[] get() { &hellip; }
+  }
+
+  // for kernels with uint result
+  //   note that the Java type "long" is a wider signed type than the unsigned script type "uint"
+  public static class result_uint {
+    public long get() { &hellip; }
+  }
+
+  // for kernels with uint[10] result
+  //   note that the Java type "long" is a wider signed type than the unsigned script type "uint"
+  public static class resultArray10_uint {
+    public long[] get() { &hellip; }
+  }
+
+  // for kernels with uint2 result
+  //   note that the Java type "Long2" is a wider signed type than the unsigned script type "uint2"
+  public static class result_uint2 {
+    public Long2 get() { &hellip; }
+  }
+
+  // for kernels with uint2[10] result
+  //   note that the Java type "Long2" is a wider signed type than the unsigned script type "uint2"
+  public static class resultArray10_uint2 {
+    public Long2[] get() { &hellip; }
+  }
+}
+</pre>
+
+<p>If <i>javaResultType</i> is an object type (including an array type), each call
+  to <code><i>javaFutureType</i>.get()</code> on the same instance will return the same
+  object.</p>
+
+<p>If <i>javaResultType</i> cannot represent all values of type <i>resultType</i>, and a
+  reduction kernel produces an unrepresentible value,
+  then <code><i>javaFutureType</i>.get()</code> throws an exception.</p>
+
+<h4 id="devec">Method 3 and <i>devecSiInXType</i></h4>
+
+<p><strong><i>devecSiInXType</i></strong> is the Java type corresponding to
+  the <i>inXType</i> of the corresponding argument of
+  the <a href="#accumulator-function">accumulator function</a>. Unless <i>inXType</i> is an
+  unsigned type or a vector type, <i>devecSiInXType</i> is the directly corresponding Java
+  type. If <i>inXType</i> is an unsigned scalar type, then <i>devecSiInXType</i> is the
+  Java type directly corresponding to the signed scalar type of the same
+  size. If <i>inXType</i> is a signed vector type, then <i>devecSiInXType</i> is the Java
+  type directly corresponding to the vector component type. If <i>inXType</i> is an unsigned
+  vector type, then <i>devecSiInXType</i> is the Java type directly corresponding to the
+  signed scalar type of the same size as the vector component type. For example:</p>
+<ul>
+<li>If <i>inXType</i> is <code>int</code>, then <i>devecSiInXType</i>
+  is <code>int</code>.</li>
+<li>If <i>inXType</i> is <code>int2</code>, then <i>devecSiInXType</i>
+  is <code>int</code>. The array is a <em>flattened</em> representation: It has twice as
+  many <em>scalar</em> Elements as the Allocation has 2-component <em>vector</em>
+  Elements. This is the same way that the <code>copyFrom()</code> methods of {@link
+  android.renderscript.Allocation} work.</li>
+<li>If <i>inXType</i> is <code>uint</code>, then <i>deviceSiInXType</i>
+  is <code>int</code>. A signed value in the Java array is interpreted as an unsigned value of
+  the same bitpattern in the Allocation. This is the same way that the <code>copyFrom()</code>
+  methods of {@link android.renderscript.Allocation} work.</li>
+<li>If <i>inXType</i> is <code>uint2</code>, then <i>deviceSiInXType</i>
+  is <code>int</code>. This is a combination of the way <code>int2</code> and <code>uint</code>
+  are handled: The array is a flattened representation, and Java array signed values are
+  interpreted as RenderScript unsigned Element values.</li>
+</ul>
+
+<p>Note that for <a href="#reduce-method-3">Method 3</a>, input types are handled differently
+than result types:</p>
+
+<ul>
+<li>A script's vector input is flattened on the Java side, whereas a script's vector result is not.</li>
+<li>A script's unsigned input is represented as a signed input of the same size on the Java
+  side, whereas a script's unsigned result is represented as a widened signed type on the Java
+  side (except in the case of <code>ulong</code>).</li>
+</ul>
+
+<h3 id="more-example">More example reduction kernels</h3>
+
+<pre id="dot-product">
+#pragma rs reduce(dotProduct) \
+  accumulator(dotProductAccum) combiner(dotProductSum)
+
+// Note: No initializer function -- therefore,
+// each accumulator data item is implicitly initialized to 0.0f.
+
+static void dotProductAccum(float *accum, float in1, float in2) {
+  *accum += in1*in2;
+}
+
+// combiner function
+static void dotProductSum(float *accum, const float *val) {
+  *accum += *val;
+}
+</pre>
+
+<pre>
+// Find a zero Element in a 2D allocation; return (-1, -1) if none
+#pragma rs reduce(fz2) \
+  initializer(fz2Init) \
+  accumulator(fz2Accum) combiner(fz2Combine)
+
+static void fz2Init(int2 *accum) { accum->x = accum->y = -1; }
+
+static void fz2Accum(int2 *accum,
+                     int inVal,
+                     int x /* special arg */,
+                     int y /* special arg */) {
+  if (inVal==0) {
+    accum->x = x;
+    accum->y = y;
+  }
+}
+
+static void fz2Combine(int2 *accum, const int2 *accum2) {
+  if (accum2->x >= 0) *accum = *accum2;
+}
+</pre>
+
+<pre>
+// Note that this kernel returns an array to Java
+#pragma rs reduce(histogram) \
+  accumulator(hsgAccum) combiner(hsgCombine)
+
+#define BUCKETS 256
+typedef uint32_t Histogram[BUCKETS];
+
+// Note: No initializer function --
+// therefore, each bucket is implicitly initialized to 0.
+
+static void hsgAccum(Histogram *h, uchar in) { ++(*h)[in]; }
+
+static void hsgCombine(Histogram *accum,
+                       const Histogram *addend) {
+  for (int i = 0; i < BUCKETS; ++i)
+    (*accum)[i] += (*addend)[i];
+}
+
+// Determines the mode (most frequently occurring value), and returns
+// the value and the frequency.
+//
+// If multiple values have the same highest frequency, returns the lowest
+// of those values.
+//
+// Shares functions with the histogram reduction kernel.
+#pragma rs reduce(mode) \
+  accumulator(hsgAccum) combiner(hsgCombine) \
+  outconverter(modeOutConvert)
+
+static void modeOutConvert(int2 *result, const Histogram *h) {
+  uint32_t mode = 0;
+  for (int i = 1; i < BUCKETS; ++i)
+    if ((*h)[i] > (*h)[mode]) mode = i;
+  result->x = mode;
+  result->y = (*h)[mode];
+}
+</pre>
diff --git a/docs/html/preview/_project.yaml b/docs/html/preview/_project.yaml
new file mode 100644
index 0000000..3ec851a
--- /dev/null
+++ b/docs/html/preview/_project.yaml
@@ -0,0 +1,5 @@
+name: "Preview"
+home_url: /preview/
+description: "Android N final SDK is now available."
+content_license: cc3-apache2
+buganizer_id: 30209417
diff --git a/docs/html/reference/_project.yaml b/docs/html/reference/_project.yaml
new file mode 100644
index 0000000..e5c26e7
--- /dev/null
+++ b/docs/html/reference/_project.yaml
@@ -0,0 +1,6 @@
+name: "Reference"
+home_url: /reference/
+description: "API Reference packages and classes."
+content_license: cc3-apache2
+buganizer_id: 30209417
+parent_project_metadata_path: /develop/_project.yaml
diff --git a/docs/html/samples/_project.yaml b/docs/html/samples/_project.yaml
new file mode 100644
index 0000000..ede5958
--- /dev/null
+++ b/docs/html/samples/_project.yaml
@@ -0,0 +1,6 @@
+name: "Samples"
+home_url: /samples/
+description: "Welcome to code samples where you can browse sample code and learn how to build different components for your applications."
+content_license: cc3-apache2
+buganizer_id: 30209417
+parent_project_metadata_path: /develop/_project.yaml
diff --git a/docs/html/topic/instant-apps/_project.yaml b/docs/html/topic/instant-apps/_project.yaml
new file mode 100644
index 0000000..e6b8cd9
--- /dev/null
+++ b/docs/html/topic/instant-apps/_project.yaml
@@ -0,0 +1,6 @@
+name: "Android Instant Apps"
+home_url: /topic/instant-apps/
+description: "An evolution in app sharing and discovery, Android Instant Apps enables Android apps to run instantly, without requiring installation."
+content_license: cc3-apache2
+buganizer_id: 30209417
+parent_project_metadata_path: /develop/_project.yaml
diff --git a/docs/html/topic/libraries/_project.yaml b/docs/html/topic/libraries/_project.yaml
new file mode 100644
index 0000000..cdf59e5
--- /dev/null
+++ b/docs/html/topic/libraries/_project.yaml
@@ -0,0 +1,6 @@
+name: "Android Libraries"
+home_url: /topic/libraries/
+description: "This section describes several useful Android libraries that are not included with the Android Framework."
+content_license: cc3-apache2
+buganizer_id: 30209417
+parent_project_metadata_path: /develop/_project.yaml
diff --git a/docs/html/topic/performance/_project.yaml b/docs/html/topic/performance/_project.yaml
new file mode 100644
index 0000000..d4202a9
--- /dev/null
+++ b/docs/html/topic/performance/_project.yaml
@@ -0,0 +1,6 @@
+name: "Performance"
+home_url: /topic/performance/
+description: "Improve your app's performance by learning how to optimize power consumption, launch times, and other important areas of performance."
+content_license: cc3-apache2
+buganizer_id: 30209417
+parent_project_metadata_path: /develop/_project.yaml
diff --git a/docs/html/training/_project.yaml b/docs/html/training/_project.yaml
new file mode 100644
index 0000000..6aa8760
--- /dev/null
+++ b/docs/html/training/_project.yaml
@@ -0,0 +1,6 @@
+name: "Training"
+home_url: /training/
+description: "Android Training provides a collection of classes that aim to help you build great apps for Android."
+content_license: cc3-apache2
+buganizer_id: 30209417
+parent_project_metadata_path: /develop/_project.yaml
diff --git a/docs/html/training/testing/ui-testing/espresso-testing.jd b/docs/html/training/testing/ui-testing/espresso-testing.jd
index 7df67e7..d3d31de 100644
--- a/docs/html/training/testing/ui-testing/espresso-testing.jd
+++ b/docs/html/training/testing/ui-testing/espresso-testing.jd
@@ -188,9 +188,9 @@
 {@code ActivityTestRule}</a> to reduce the amount of boilerplate code you need to write. By using
 <a href="{@docRoot}reference/android/support/test/rule/ActivityTestRule.html">
 {@code ActivityTestRule}</a>, the testing framework launches the activity under test
-before each test method annotated with {@code &#64;Test} and before any method annotated with
-{@code &#64;Before}. The framework handles shutting down the activity after the test finishes
-and all methods annotated with {@code &#64;After} are run.</p>
+before each test method annotated with <code>&#64;Test</code> and before any method annotated with
+<code>&#64;Before</code>. The framework handles shutting down the activity after the test finishes
+and all methods annotated with <code>&#64;After</code> are run.</p>
 
 <pre>
 package com.example.android.testing.espresso.BasicSample;
diff --git a/docs/html/training/wearables/notifications/creating.jd b/docs/html/training/wearables/notifications/creating.jd
index 6f8497a..5663b58 100644
--- a/docs/html/training/wearables/notifications/creating.jd
+++ b/docs/html/training/wearables/notifications/creating.jd
@@ -28,7 +28,7 @@
 
 <p class="note"><strong>Note:</strong>
 Notifications using {@link android.widget.RemoteViews} are stripped of custom
-layouts and the wearable only displays the text and icons. However, you can create
+layouts and the wearable only displays the text and icons. However, you can 
 <a href="{@docRoot}training/wearables/apps/layouts.html#CustomNotifications">create custom notifications</a>
 that use custom card layouts by creating a wearable app that runs on the wearable device.</p>
 
diff --git a/docs/html/wear/_project.yaml b/docs/html/wear/_project.yaml
new file mode 100644
index 0000000..2a94274
--- /dev/null
+++ b/docs/html/wear/_project.yaml
@@ -0,0 +1,5 @@
+name: "Wear"
+home_url: /wear/
+description: "Small, powerful devices, worn on the body. Useful information when you need it most."
+content_license: cc3-apache2
+buganizer_id: 30209417
diff --git a/docs/html/work/_project.yaml b/docs/html/work/_project.yaml
new file mode 100644
index 0000000..9db0466
--- /dev/null
+++ b/docs/html/work/_project.yaml
@@ -0,0 +1,5 @@
+name: "Work"
+home_url: /work/
+description: "Develop apps for Android for Work to take advantage of security and management features built into Android."
+content_license: cc3-apache2
+buganizer_id: 30209417
diff --git a/docs/html/work/managed-configurations.jd b/docs/html/work/managed-configurations.jd
index 91c0637..6de4d8b 100644
--- a/docs/html/work/managed-configurations.jd
+++ b/docs/html/work/managed-configurations.jd
@@ -35,7 +35,10 @@
 </ul>
 
 <p>
-  This guide shows how to implement these configuration settings in your app.
+  This guide shows how to implement managed configuration settings in
+  your app. If you're an EMM developer, refer to the
+  <a href="https://developers.google.com/android/work/build-dpc"
+  >Build a Device Policy Controller</a> guide.
 </p>
 
 <p class="note">
@@ -71,8 +74,8 @@
 
 <ul>
   <li>Declare the managed configurations in your app manifest. Doing
-  so allows the enterprise administrator to read the app's
-  configurations through Google Play APIs.
+    so allows the enterprise administrator to read the app's
+    configurations through Google Play APIs.
   </li>
 
   <li>Whenever the app resumes, use the {@link
@@ -82,11 +85,11 @@
   </li>
 
   <li>Listen for the
-  {@link android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED
-  ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent. When you receive this
-  broadcast, check the {@link android.content.RestrictionsManager} to see what
-  the current managed configurations are, and make any necessary changes to your
-  app's behavior.
+    {@link android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED
+    ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent. When you receive this
+    broadcast, check the {@link android.content.RestrictionsManager} to see what
+    the current managed configurations are, and make any necessary changes to your
+    app's behavior.
   </li>
 </ul>
 
@@ -96,11 +99,11 @@
 
 <p>
   Your app can support any managed configuration you want to define. You declare the
-  app's managed configurations in a <em>managed configurations file</em>, and declare the
-  configurations file in the manifest. Creating a configurations file allows other
-  apps to examine the managed configurations your app provides. Enterprise Mobility
-  Management (EMM) partners can read your app's configurations by using Google
-  Play APIs.
+  app's managed configurations in a <em>managed configurations file</em>, and declare
+  the configurations file in the manifest. Creating a configurations file allows
+  other apps to examine the managed configurations your app provides. Enterprise
+  Mobility Management (EMM) partners can read your app's configurations by using
+  Google Play APIs.
 </p>
 
 <p>
@@ -138,6 +141,14 @@
 </p>
 
 <p>
+  The managed configuration provider can query the app to find details
+  on the app's available configurations, including their description
+  text. The configurations provider and enterprise administrator can
+  change your app's managed configurations at any time, even when the
+  app is not running.
+</p>
+
+<p>
   For example, suppose your app can be remotely configured to allow or forbid
   it to download data over a cellular connection. Your app could have a
   <code>&lt;restriction&gt;</code> element like this:
@@ -145,7 +156,7 @@
 
 <pre>
 &lt;?xml version="1.0" encoding="utf-8"?&gt;
-&lt;restrictions xmlns:android="http://schemas.android.com/apk/res/android" &gt;
+&lt;restrictions xmlns:android="http://schemas.android.com/apk/res/android"&gt;
 
   &lt;restriction
     android:key="downloadOnCellular"
@@ -158,11 +169,6 @@
 </pre>
 
 <p>
-  The supported types for the <code>android:restrictionType</code> element are
-  documented in the reference for {@link android.content.RestrictionsManager}.
-</p>
-
-<p>
   You use each configuration's <code>android:key</code> attribute to
   read its value from a managed configuration bundle. For this reason,
   each configuration must have a unique key string, and the string
@@ -172,19 +178,145 @@
 <p class="note">
   <strong>Note:</strong> In a production app, <code>android:title</code> and
   <code>android:description</code> should be drawn from a localized resource
-  file, as described in <a href=
-  "{@docRoot}guide/topics/resources/localization.html">Localizing with
-  Resources</a>.
+  file, as described in
+  <a href="{@docRoot}guide/topics/resources/localization.html"
+  >Localizing with Resources</a>.
 </p>
 
-<p>
-  The managed configuration provider can query the app to find details
-  on the app's available configurations, including their description
-  text. Configurations providers and enterprise administrators can
-  change your app's managed configurations at any time, even when the
-  app is not running.
+<p id="nested-restrictions">
+  An app can define one or multiple nested restriction elements using
+  the restriction types
+  {@link android.content.RestrictionEntry#TYPE_BUNDLE bundle} and
+  {@link android.content.RestrictionEntry#TYPE_BUNDLE_ARRAY bundle_array}.
+  For example, an app with multiple VPN connection options could define
+  each VPN server configuration in a bundle, with multiple bundles grouped
+  together in a bundle array:
 </p>
 
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;restrictions xmlns:android="http://schemas.android.com/apk/res/android" &gt;
+
+  &lt;restriction
+    android:key="vpn_configuration_list"
+    android:restrictionType="bundle_array"&gt;
+    &lt;restriction
+      android:key="vpn_configuration"
+      android:restrictionType="bundle"&gt;
+      &lt;restriction
+        android:key="vpn_server"
+        android:restrictionType="string"/&gt;
+      &lt;restriction
+        android:key="vpn_username"
+        android:restrictionType="string"/&gt;
+      &lt;restriction
+        android:key="vpn_password"
+        android:restrictionType="string"/&gt;
+    &lt;/restriction&gt;
+  &lt;/restriction&gt;
+
+&lt;/restrictions&gt;
+</pre>
+
+<p>
+  The supported types for the <code>android:restrictionType</code> element
+  are listed in <a href="#restriction-types">Table 1</a> and documented in
+  the reference for {@link android.content.RestrictionsManager} and
+  {@link android.content.RestrictionEntry}.
+</p>
+
+<p class="table-caption" id="restriction-types">
+  <strong>Table 1.</strong> Restriction entry types and usage.
+</p>
+<table>
+  <tbody>
+    <tr>
+      <th>Type</th>
+      <th>android:restrictionType</th>
+      <th>Typical usage</th>
+    </tr>
+    <tr>
+      <td>
+        {@link android.content.RestrictionEntry#TYPE_BOOLEAN TYPE_BOOLEAN}
+      </td>
+      <td><code>"bool"</code></td>
+      <td>
+        A boolean value, true or false.
+      </td>
+    </tr>
+    <tr>
+      <td>
+        {@link android.content.RestrictionEntry#TYPE_STRING TYPE_STRING}
+      </td>
+      <td><code>"string"</code></td>
+      <td>
+        A string value, such as a name.
+      </td>
+    </tr>
+    <tr>
+      <td>
+        {@link android.content.RestrictionEntry#TYPE_INTEGER TYPE_INTEGER}
+      </td>
+      <td><code>"integer"</code></td>
+      <td>
+        An integer with a value from
+        {@link java.lang.Integer#MIN_VALUE MIN_VALUE} to
+        {@link java.lang.Integer#MAX_VALUE MAX_VALUE}.
+      </td>
+    </tr>
+    <tr>
+      <td>
+        {@link android.content.RestrictionEntry#TYPE_CHOICE TYPE_CHOICE}
+      </td>
+      <td><code>"choice"</code></td>
+      <td>
+        A string value, typically presented as a single-select list.
+      </td>
+    </tr>
+    <tr>
+      <td>
+        {@link android.content.RestrictionEntry#TYPE_MULTI_SELECT TYPE_MULTI_SELECT}
+      </td>
+      <td><code>"multi-select"</code></td>
+      <td>
+        Use this for presenting a multi-select list where more than
+        one entry can be selected, such as for choosing specific
+        titles to white-list.
+      </td>
+    </tr>
+    <tr>
+      <td>
+        {@link android.content.RestrictionEntry#TYPE_NULL TYPE_NULL}
+      </td>
+      <td><code>"hidden"</code></td>
+      <td>
+        Hidden restriction type. Use this type for information that
+        needs to be transferred across but shouldn't be presented to
+        the user in the UI. Stores a single string value.
+      </td>
+    </tr>
+    <tr>
+      <td>{@link android.content.RestrictionEntry#TYPE_BUNDLE TYPE_BUNDLE}</td>
+      <td><code>"bundle"</code></td>
+      <td>
+        Use this for storing {@link android.os.Bundle bundles} of
+        restrictions. Available in Android 6.0 (API level 23).
+      </td>
+    </tr>
+    <tr>
+      <td>
+        {@link android.content.RestrictionEntry#TYPE_BUNDLE_ARRAY TYPE_BUNDLE_ARRAY}
+      </td>
+      <td><code>"bundle_array"</code></td>
+      <td>
+        Use this for storing arrays of restriction
+        <a href="{@docRoot}reference/android/os/Bundle.html"
+        >bundles</a>. Available in Android 6.0 (API level 23).
+      </td>
+    </tr>
+  </tbody>
+</table>
+
 <h2 id="check-configuration">
   Check Managed Configurations
 </h2>
@@ -292,11 +424,10 @@
 <pre>
 boolean appCanUseCellular;
 
-if appRestrictions.containsKey("downloadOnCellular") {
+if (appRestrictions.containsKey("downloadOnCellular")) {
     appCanUseCellular = appRestrictions.getBoolean("downloadOnCellular");
 } else {
-    // here, cellularDefault is a boolean set with the restriction's
-    // default value
+    // cellularDefault is a boolean using the restriction's default value
     appCanUseCellular = cellularDefault;
 }
 
@@ -305,6 +436,37 @@
     // ...show appropriate notices to user
 }</pre>
 
+<p>
+  To apply multiple <a href="#nested-restrictions">nested restrictions</a>, read
+  the {@link android.content.RestrictionEntry#TYPE_BUNDLE_ARRAY bundle_array}
+  restriction entry as a collection of {@link android.os.Parcelable} objects
+  and cast as a {@link android.os.Bundle}. In this example, each VPN's configuration
+  data is parsed and used to build a list of server connection choices:
+</p>
+
+<pre>
+// VpnConfig is a sample class used store config data, not defined
+List&lt;VpnConfig&gt; vpnConfigs = new ArrayList&lt;&gt;();
+
+Parcelable[] parcelables =
+    appRestrictions.getParcelableArray("vpn_configuration_list");
+
+if (parcelables != null && parcelables.length > 0) {
+    // iterate parcelables and cast as bundle
+    for (int i = 0; i < parcelables.length; i++) {
+        Bundle vpnConfigBundle = (Bundle) parcelables[i];
+        // parse bundle data and store in VpnConfig array
+        vpnConfigs.add(new VpnConfig()
+            .setServer(vpnConfigBundle.getString("vpn_server"))
+            .setUsername(vpnConfigBundle.getString("vpn_username"))
+            .setPassword(vpnConfigBundle.getString("vpn_password")));
+    }
+}
+
+if (!vpnConfigs.isEmpty()) {
+    // ...choose a VPN configuration or prompt user to select from list
+}</pre>
+
 <h2 id="listen-configuration">
   Listen for Managed Configuration Changes
 </h2>
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 841ec78..9b0be49 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -219,9 +219,9 @@
     for (size_t i = 1; i < mSwapHistory.size(); i++) {
         auto& swapB = mSwapHistory[i];
 
-        // If there's a frameInterval gap we effectively already dropped a frame,
+        // If there's a multi-frameInterval gap we effectively already dropped a frame,
         // so consider the queue healthy.
-        if (swapA.swapCompletedTime - swapB.swapCompletedTime > frameInterval) {
+        if (swapA.swapCompletedTime - swapB.swapCompletedTime > frameInterval * 3) {
             return false;
         }
 
@@ -296,12 +296,17 @@
             // Already drew for this vsync pulse, UI draw request missed
             // the deadline for RT animations
             info.out.canDrawThisFrame = false;
-        } else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos()) {
-            // It's been at least an entire frame interval, assume
-            // the buffer queue is fine
+        } else if (vsyncDelta >= mRenderThread.timeLord().frameIntervalNanos() * 3
+                || (latestVsync - mLastDropVsync) < 500_ms) {
+            // It's been several frame intervals, assume the buffer queue is fine
+            // or the last drop was too recent
             info.out.canDrawThisFrame = true;
         } else {
             info.out.canDrawThisFrame = !isSwapChainStuffed();
+            if (!info.out.canDrawThisFrame) {
+                // dropping frame
+                mLastDropVsync = mRenderThread.timeLord().latestVsync();
+            }
         }
     } else {
         info.out.canDrawThisFrame = true;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 42e9be3..688bf2a 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -200,6 +200,9 @@
     RingBuffer<SwapHistory, 3> mSwapHistory;
     int64_t mFrameNumber = -1;
 
+    // last vsync for a dropped frame due to stuffed queue
+    nsecs_t mLastDropVsync = 0;
+
     bool mOpaque;
     BakedOpRenderer::LightInfo mLightInfo;
     FrameBuilder::LightGeometry mLightGeometry = { {0, 0, 0}, 0 };
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 2c98e98..563d9cb 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -349,6 +349,11 @@
     public static final String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
     /** Type is int. DNG Specification 1.4.0.0. Section 4 */
     public static final String TAG_DEFAULT_CROP_SIZE = "DefaultCropSize";
+    /** Type is int. ORF Specification. http://www.exiv2.org/tags-olympus.html */
+    public static final String TAG_THUMBNAIL_IMAGE = "ThumbnailImage";
+    public static final String TAG_PREVIEW_IMAGE_START = "PreviewImageStart";
+    public static final String TAG_PREVIEW_IMAGE_LENGTH = "PreviewImageLength";
+    public static final String TAG_ASPECT_FRAME = "AspectFrame";
 
     /**
      * Private tags used for pointing the other IFD offsets.
@@ -360,6 +365,10 @@
     private static final String TAG_GPS_INFO_IFD_POINTER = "GPSInfoIFDPointer";
     private static final String TAG_INTEROPERABILITY_IFD_POINTER = "InteroperabilityIFDPointer";
     private static final String TAG_SUB_IFD_POINTER = "SubIFDPointer";
+    // Proprietary pointer tags used for ORF file format.
+    // See http://www.exiv2.org/tags-olympus.html
+    private static final String TAG_CAMERA_SETTINGS_IFD_POINTER = "CameraSettingsIFDPointer";
+    private static final String TAG_IMAGE_PROCESSING_IFD_POINTER = "ImageProcessingIFDPointer";
 
     // Private tags used for thumbnail information.
     private static final String TAG_HAS_THUMBNAIL = "HasThumbnail";
@@ -389,12 +398,24 @@
     private static final int SIGNATURE_CHECK_SIZE = 5000;
 
     private static final byte[] JPEG_SIGNATURE = new byte[] {(byte) 0xff, (byte) 0xd8, (byte) 0xff};
-    private static final int JPEG_SIGNATURE_SIZE = 3;
     private static final String RAF_SIGNATURE = "FUJIFILMCCD-RAW";
-    private static final int RAF_SIGNATURE_SIZE = 15;
     private static final int RAF_OFFSET_TO_JPEG_IMAGE_OFFSET = 84;
     private static final int RAF_INFO_SIZE = 160;
-    private int mRafJpegOffset, mRafJpegLength, mRafCfaHeaderOffset, mRafCfaHeaderLength;
+    private static final int RAF_JPEG_LENGTH_VALUE_SIZE = 4;
+
+    // See http://fileformats.archiveteam.org/wiki/Olympus_ORF
+    private static final short ORF_SIGNATURE_1 = 0x4f52;
+    private static final short ORF_SIGNATURE_2 = 0x5352;
+    // There are two formats for Olympus Makernote Headers. Each has different identifiers and
+    // offsets to the actual data.
+    // See http://www.exiv2.org/makernote.html#R1
+    private static final byte[] ORF_MAKER_NOTE_HEADER_1 = new byte[] {(byte) 0x4f, (byte) 0x4c,
+            (byte) 0x59, (byte) 0x4d, (byte) 0x50, (byte) 0x00}; // "OLYMP\0"
+    private static final byte[] ORF_MAKER_NOTE_HEADER_2 = new byte[] {(byte) 0x4f, (byte) 0x4c,
+            (byte) 0x59, (byte) 0x4d, (byte) 0x50, (byte) 0x55, (byte) 0x53, (byte) 0x00,
+            (byte) 0x49, (byte) 0x49}; // "OLYMPUS\0II"
+    private static final int ORF_MAKER_NOTE_HEADER_1_SIZE = 8;
+    private static final int ORF_MAKER_NOTE_HEADER_2_SIZE = 12;
 
     private static SimpleDateFormat sFormatter;
 
@@ -424,6 +445,8 @@
     private static final int IFD_FORMAT_SRATIONAL = 10;
     private static final int IFD_FORMAT_SINGLE = 11;
     private static final int IFD_FORMAT_DOUBLE = 12;
+    // Format indicating a new IFD entry (See Adobe PageMaker® 6.0 TIFF Technical Notes, "New Tag")
+    private static final int IFD_FORMAT_IFD = 13;
     // Names for the data formats for debugging purpose.
     private static final String[] IFD_FORMAT_NAMES = new String[] {
             "", "BYTE", "STRING", "USHORT", "ULONG", "URATIONAL", "SBYTE", "UNDEFINED", "SSHORT",
@@ -431,7 +454,7 @@
     };
     // Sizes of the components of each IFD value format
     private static final int[] IFD_FORMAT_BYTES_PER_FORMAT = new int[] {
-            0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8
+            0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 1
     };
     private static final byte[] EXIF_ASCII_PREFIX = new byte[] {
             0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0
@@ -1043,10 +1066,25 @@
             new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
             new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG)
     };
+
     // RAF file tag (See piex.cc line 372)
     private static final ExifTag TAG_RAF_IMAGE_SIZE =
             new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT);
 
+    // ORF file tags (See http://www.exiv2.org/tags-olympus.html)
+    private static final ExifTag[] ORF_MAKER_NOTE_TAGS = new ExifTag[] {
+            new ExifTag(TAG_THUMBNAIL_IMAGE, 256, IFD_FORMAT_UNDEFINED),
+            new ExifTag(TAG_CAMERA_SETTINGS_IFD_POINTER, 8224, IFD_FORMAT_ULONG),
+            new ExifTag(TAG_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_ULONG)
+    };
+    private static final ExifTag[] ORF_CAMERA_SETTINGS_TAGS = new ExifTag[] {
+            new ExifTag(TAG_PREVIEW_IMAGE_START, 257, IFD_FORMAT_ULONG),
+            new ExifTag(TAG_PREVIEW_IMAGE_LENGTH, 258, IFD_FORMAT_ULONG)
+    };
+    private static final ExifTag[] ORF_IMAGE_PROCESSING_TAGS = new ExifTag[] {
+            new ExifTag(TAG_ASPECT_FRAME, 4371, IFD_FORMAT_USHORT)
+    };
+
     // See JEITA CP-3451C Section 4.6.3: Exif-specific IFD.
     // The following values are used for indicating pointers to the other Image File Directories.
 
@@ -1057,21 +1095,28 @@
     private static final int IFD_INTEROPERABILITY_HINT = 3;
     private static final int IFD_THUMBNAIL_HINT = 4;
     private static final int IFD_PREVIEW_HINT = 5;
+    private static final int ORF_MAKER_NOTE_HINT = 6;
+    private static final int ORF_CAMERA_SETTINGS_HINT = 7;
+    private static final int ORF_IMAGE_PROCESSING_HINT = 8;
     // List of Exif tag groups
     private static final ExifTag[][] EXIF_TAGS = new ExifTag[][] {
             IFD_TIFF_TAGS, IFD_EXIF_TAGS, IFD_GPS_TAGS, IFD_INTEROPERABILITY_TAGS,
-            IFD_THUMBNAIL_TAGS, IFD_TIFF_TAGS
+            IFD_THUMBNAIL_TAGS, IFD_TIFF_TAGS, ORF_MAKER_NOTE_TAGS, ORF_CAMERA_SETTINGS_TAGS,
+            ORF_IMAGE_PROCESSING_TAGS
     };
     // List of tags for pointing to the other image file directory offset.
-    private static final ExifTag[] IFD_POINTER_TAGS = new ExifTag[] {
+    private static final ExifTag[] EXIF_POINTER_TAGS = new ExifTag[] {
             new ExifTag(TAG_SUB_IFD_POINTER, 330, IFD_FORMAT_ULONG),
             new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
             new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
-            new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG)
+            new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG),
+            new ExifTag(TAG_CAMERA_SETTINGS_IFD_POINTER, 8224, IFD_FORMAT_BYTE),
+            new ExifTag(TAG_IMAGE_PROCESSING_IFD_POINTER, 8256, IFD_FORMAT_BYTE)
     };
-    // List of indices of the indicated tag groups according to the IFD_POINTER_TAGS
-    private static final int[] IFD_POINTER_TAG_HINTS = new int[] {
-            IFD_TIFF_HINT, IFD_EXIF_HINT, IFD_GPS_HINT, IFD_INTEROPERABILITY_HINT
+    // List of indices of the indicated tag groups according to the EXIF_POINTER_TAGS
+    private static final int[] EXIF_POINTER_TAG_HINTS = new int[] {
+            IFD_TIFF_HINT, IFD_EXIF_HINT, IFD_GPS_HINT, IFD_INTEROPERABILITY_HINT,
+            ORF_CAMERA_SETTINGS_HINT, ORF_IMAGE_PROCESSING_HINT
     };
     // Tags for indicating the thumbnail offset and length
     private static final ExifTag JPEG_INTERCHANGE_FORMAT_TAG =
@@ -1162,6 +1207,10 @@
     private int mThumbnailOffset;
     private int mThumbnailLength;
     private byte[] mThumbnailBytes;
+    private int mExifOffset;
+    private int mOrfMakerNoteOffset;
+    private int mOrfThumbnailOffset;
+    private int mOrfThumbnailLength;
 
     // Pattern to check non zero timestamp
     private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
@@ -1537,18 +1586,35 @@
 
                 switch (mMimeType) {
                     case IMAGE_TYPE_JPEG: {
-                        getJpegAttributes(in, IFD_TIFF_HINT);
+                        getJpegAttributes(in, 0, IFD_TIFF_HINT); // 0 is offset
                         break;
                     }
                     case IMAGE_TYPE_RAF: {
                         getRafAttributes(in);
                         break;
                     }
-                    default: {
+                    case IMAGE_TYPE_ORF: {
+                        getOrfAttributes(in);
+                        break;
+                    }
+                    case IMAGE_TYPE_ARW:
+                    case IMAGE_TYPE_CR2:
+                    case IMAGE_TYPE_DNG:
+                    case IMAGE_TYPE_NEF:
+                    case IMAGE_TYPE_NRW:
+                    case IMAGE_TYPE_PEF:
+                    case IMAGE_TYPE_RW2:
+                    case IMAGE_TYPE_SRW:
+                    case IMAGE_TYPE_UNKNOWN: {
                         getRawAttributes(in);
                         break;
                     }
+                    default: {
+                        break;
+                    }
                 }
+                // Set thumbnail image offset and length
+                setThumbnailData(in);
             } else {
                 if (mAssetInputStream != null) {
                     long asset = mAssetInputStream.getNativeAsset();
@@ -1561,26 +1627,28 @@
                         return;
                     }
                 } else {
-                    in.mark(JPEG_SIGNATURE_SIZE);
-                    byte[] signatureBytes = new byte[JPEG_SIGNATURE_SIZE];
-                    if (in.read(signatureBytes) != JPEG_SIGNATURE_SIZE) {
+                    in.mark(JPEG_SIGNATURE.length);
+                    byte[] signatureBytes = new byte[JPEG_SIGNATURE.length];
+                    if (in.read(signatureBytes) != JPEG_SIGNATURE.length) {
                         throw new EOFException();
                     }
                     in.reset();
-                    if (!isJpegInputStream(signatureBytes) && handleRawResult(
+                    if (!isJpegFormat(signatureBytes) && handleRawResult(
                             nativeGetRawAttributesFromInputStream(in))) {
                         return;
                     }
                 }
                 // Process JPEG input stream
-                getJpegAttributes(in, IFD_TIFF_HINT);
+                getJpegAttributes(in, 0, IFD_TIFF_HINT);
             }
         } catch (IOException e) {
             // Ignore exceptions in order to keep the compatibility with the old versions of
             // ExifInterface.
-            Log.w(TAG, "Invalid image: ExifInterface got an unsupported image format file"
-                    + "(ExifInterface supports JPEG and some RAW image formats only) "
-                    + "or a corrupted JPEG file to ExifInterface.", e);
+            if (DEBUG) {
+                Log.w(TAG, "Invalid image: ExifInterface got an unsupported image format file"
+                        + "(ExifInterface supports JPEG and some RAW image formats only) "
+                        + "or a corrupted JPEG file to ExifInterface.", e);
+            }
         } finally {
             addDefaultValuesForCompatibility();
 
@@ -1590,17 +1658,11 @@
         }
     }
 
-    private static boolean isJpegInputStream(byte[] signatureBytes) throws IOException {
-        for (int i = 0; i < JPEG_SIGNATURE_SIZE; i++) {
-            if (signatureBytes[i] != JPEG_SIGNATURE[i]) {
-                return false;
-            }
-        }
-        return true;
-    }
-
     private boolean handleRawResult(HashMap map) {
         if (map == null) {
+            if (DEBUG) {
+                Log.d(TAG, "Raw image file not detected");
+            }
             return false;
         }
 
@@ -1921,36 +1983,101 @@
     // Checks the type of image file
     private int getMimeType(BufferedInputStream in) throws IOException {
         in.mark(SIGNATURE_CHECK_SIZE);
-        byte[] signatureBytes = new byte[SIGNATURE_CHECK_SIZE];
-        if (in.read(signatureBytes) != SIGNATURE_CHECK_SIZE) {
+        byte[] signatureCheckBytes = new byte[SIGNATURE_CHECK_SIZE];
+        if (in.read(signatureCheckBytes) != SIGNATURE_CHECK_SIZE) {
             throw new EOFException();
         }
         in.reset();
-        if (isJpegInputStream(signatureBytes)) {
+        if (isJpegFormat(signatureCheckBytes)) {
             return IMAGE_TYPE_JPEG;
-        } else if (isRafInputStream(signatureBytes)) {
+        } else if (isRafFormat(signatureCheckBytes)) {
             return IMAGE_TYPE_RAF;
+        } else if (isOrfFormat(signatureCheckBytes)) {
+            return IMAGE_TYPE_ORF;
         }
         return IMAGE_TYPE_UNKNOWN;
     }
 
     /**
+     * This method looks at the first 3 bytes to determine if this file is a JPEG file.
+     * See http://www.media.mit.edu/pia/Research/deepview/exif.html, "JPEG format and Marker"
+     */
+    private static boolean isJpegFormat(byte[] signatureCheckBytes) throws IOException {
+        for (int i = 0; i < JPEG_SIGNATURE.length; i++) {
+            if (signatureCheckBytes[i] != JPEG_SIGNATURE[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * This method looks at the first 15 bytes to determine if this file is a RAF file.
+     * There is no official specification for RAF files from Fuji, but there is an online archive of
+     * image file specifications:
+     * http://fileformats.archiveteam.org/wiki/Fujifilm_RAF
+     */
+    private boolean isRafFormat(byte[] signatureCheckBytes) throws IOException {
+        byte[] rafSignatureBytes = RAF_SIGNATURE.getBytes();
+        for (int i = 0; i < rafSignatureBytes.length; i++) {
+            if (signatureCheckBytes[i] != rafSignatureBytes[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * ORF has a similar structure to TIFF but it contains a different signature at the TIFF Header.
+     * This method looks at the 2 bytes following the Byte Order bytes to determine if this file is
+     * an ORF file.
+     * There is no official specification for ORF files from Olympus, but there is an online archive
+     * of image file specifications:
+     * http://fileformats.archiveteam.org/wiki/Olympus_ORF
+     */
+    private boolean isOrfFormat(byte[] signatureCheckBytes) throws IOException {
+        ByteOrderAwarenessDataInputStream signatureInputStream =
+                new ByteOrderAwarenessDataInputStream(signatureCheckBytes);
+        // Read byte order
+        mExifByteOrder = readByteOrder(signatureInputStream);
+        // Set byte order
+        signatureInputStream.setByteOrder(mExifByteOrder);
+
+        short orfSignature = signatureInputStream.readShort();
+        if (orfSignature == ORF_SIGNATURE_1 || orfSignature == ORF_SIGNATURE_2) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Loads EXIF attributes from a JPEG input stream.
      *
      * @param inputStream The input stream that starts with the JPEG data.
+     * @param jpegOffset The offset value in input stream for JPEG data.
      * @param imageTypes The image type from which to retrieve metadata. Use IFD_TIFF_HINT for
      *                   primary image, IFD_PREVIEW_HINT for preview image, and
      *                   IFD_THUMBNAIL_HINT for thumbnail image.
      * @throws IOException If the data contains invalid JPEG markers, offsets, or length values.
      */
-    private void getJpegAttributes(InputStream inputStream, int imageType) throws IOException {
+    private void getJpegAttributes(InputStream inputStream, int jpegOffset, int imageType)
+            throws IOException {
         // See JPEG File Interchange Format Specification, "JFIF Specification"
         if (DEBUG) {
             Log.d(TAG, "getJpegAttributes starting with: " + inputStream);
         }
+
         DataInputStream dataInputStream = new DataInputStream(inputStream);
+        // Mark current position to reset after retrieving data
+        dataInputStream.mark(dataInputStream.available());
+
+        // Skip to JPEG data
+        if (dataInputStream.skip(jpegOffset) != jpegOffset) {
+            throw new IOException("Invalid JPEG offset");
+        }
+        int bytesRead = jpegOffset;
+
         byte marker;
-        int bytesRead = 0;
         if ((marker = dataInputStream.readByte()) != MARKER) {
             throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
         }
@@ -2010,13 +2137,17 @@
                     if (DEBUG) {
                         Log.d(TAG, "readExifSegment with a byte array (length: " + length + ")");
                     }
+                    // Save offset values for retrieveJPEGThumbnail() function
+                    mExifOffset = bytesRead;
+
                     byte[] bytes = new byte[length];
                     if (dataInputStream.read(bytes) != length) {
                         throw new IOException("Invalid exif");
                     }
-                    readExifSegment(bytes, bytesRead, imageType);
                     bytesRead += length;
                     length = 0;
+
+                    readExifSegment(bytes, imageType);
                     break;
                 }
 
@@ -2069,14 +2200,16 @@
             }
             bytesRead += length;
         }
+        // Reset dataInputStream to marked position
+        dataInputStream.reset();
     }
 
     private void getRawAttributes(InputStream in) throws IOException {
-        int bytesRead = 0;
         int totalBytes = in.available();
         byte[] exifBytes = new byte[totalBytes];
         in.mark(in.available());
         in.read(exifBytes);
+        in.reset();
 
         ByteOrderAwarenessDataInputStream dataInputStream =
                 new ByteOrderAwarenessDataInputStream(exifBytes);
@@ -2114,29 +2247,11 @@
                 mAttributes[IFD_PREVIEW_HINT] = new HashMap();
             }
         }
-        // Process thumbnail.
-        processThumbnail(dataInputStream, 0, exifBytes.length);
     }
 
     /**
      * RAF files contains a JPEG and a CFA data.
      * The JPEG contains two images, a preview and a thumbnail, while the CFA contains a RAW image.
-     * This method looks at the first 160 bytes to determine if this file is a RAF file.
-     * There is no official specification for RAF files from Fuji, but there is an online archive of
-     * image file specifications:
-     * http://fileformats.archiveteam.org/wiki/Fujifilm_RAF
-     */
-    private boolean isRafInputStream(byte[] signatureBytes) throws IOException {
-        byte[] rafSignatureBytes = RAF_SIGNATURE.getBytes();
-        for (int i = 0; i < RAF_SIGNATURE_SIZE; i++) {
-            if (signatureBytes[i] != rafSignatureBytes[i]) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
      * This method looks at the first 160 bytes of a RAF file to retrieve the offset and length
      * values for the JPEG and CFA data.
      * Using that data, it parses the JPEG data to retrieve the preview and thumbnail image data,
@@ -2148,37 +2263,37 @@
         in.mark(RAF_INFO_SIZE);
         in.skip(RAF_OFFSET_TO_JPEG_IMAGE_OFFSET);
         byte[] jpegOffsetBytes = new byte[4];
-        byte[] jpegLengthBytes = new byte[4];
         byte[] cfaHeaderOffsetBytes = new byte[4];
         byte[] cfaHeaderLengthBytes = new byte[4];
         in.read(jpegOffsetBytes);
-        in.read(jpegLengthBytes);
+        // Skip JPEG length value since it is not needed
+        in.skip(RAF_JPEG_LENGTH_VALUE_SIZE);
         in.read(cfaHeaderOffsetBytes);
         in.read(cfaHeaderLengthBytes);
-        mRafJpegOffset = ByteBuffer.wrap(jpegOffsetBytes).getInt();
-        mRafJpegLength = ByteBuffer.wrap(jpegLengthBytes).getInt();
-        mRafCfaHeaderOffset = ByteBuffer.wrap(cfaHeaderOffsetBytes).getInt();
-        mRafCfaHeaderLength = ByteBuffer.wrap(cfaHeaderLengthBytes).getInt();
+        int rafJpegOffset = ByteBuffer.wrap(jpegOffsetBytes).getInt();
+        int rafCfaHeaderOffset = ByteBuffer.wrap(cfaHeaderOffsetBytes).getInt();
+        int rafCfaHeaderLength = ByteBuffer.wrap(cfaHeaderLengthBytes).getInt();
         in.reset();
 
         // Retrieve JPEG image metadata
-        in.mark(mRafJpegOffset + mRafJpegLength);
-        in.skip(mRafJpegOffset);
-        getJpegAttributes(in, IFD_PREVIEW_HINT);
-        in.reset();
+        getJpegAttributes(in, rafJpegOffset, IFD_PREVIEW_HINT);
 
         // Skip to CFA header offset.
         // A while loop is used because the skip method may not be able to skip the requested amount
         // at once because the size of the buffer may be restricted.
-        int totalSkip = mRafCfaHeaderOffset;
+        in.mark(rafCfaHeaderOffset + rafCfaHeaderLength);
+        int totalSkip = rafCfaHeaderOffset;
         while (totalSkip > 0) {
             long skipped = in.skip(totalSkip);
             totalSkip -= skipped;
         }
 
         // Retrieve primary image length/width values, if TAG_RAF_IMAGE_SIZE exists
-        byte[] exifBytes = new byte[mRafCfaHeaderLength];
-        int read = in.read(exifBytes);
+        byte[] exifBytes = new byte[rafCfaHeaderLength];
+        if (in.read(exifBytes) != rafCfaHeaderLength) {
+            throw new EOFException();
+        }
+        in.reset();
         ByteOrderAwarenessDataInputStream dataInputStream =
                 new ByteOrderAwarenessDataInputStream(exifBytes);
         int numberOfDirectoryEntry = dataInputStream.readInt();
@@ -2209,6 +2324,91 @@
         }
     }
 
+    /**
+     * ORF files contains a primary image data and a MakerNote data that contains preview/thumbnail
+     * images. Both data takes the form of IFDs and can therefore be read with the
+     * readImageFileDirectory() method.
+     * This method reads all the necessary data and updates the primary/preview/thumbnail image
+     * information according to the GetOlympusPreviewImage() method in piex.cc.
+     * For data format details, see the following:
+     * http://fileformats.archiveteam.org/wiki/Olympus_ORF
+     * https://libopenraw.freedesktop.org/wiki/Olympus_ORF
+     */
+    private void getOrfAttributes(InputStream in) throws IOException {
+        // Retrieve primary image data
+        // Other Exif data will be located in the Makernote.
+        getRawAttributes(in);
+
+        // Additionally retrieve preview/thumbnail information from MakerNote tag, which contains
+        // proprietary tags and therefore does not have offical documentation
+        // See GetOlympusPreviewImage() in piex.cc & http://www.exiv2.org/tags-olympus.html
+        ExifAttribute makerNoteAttribute =
+                (ExifAttribute) mAttributes[IFD_EXIF_HINT].get(TAG_MAKER_NOTE);
+        if (makerNoteAttribute != null) {
+            // Create an ordered DataInputStream for MakerNote
+            ByteOrderAwarenessDataInputStream makerNoteDataInputStream =
+                    new ByteOrderAwarenessDataInputStream(makerNoteAttribute.bytes);
+            makerNoteDataInputStream.setByteOrder(mExifByteOrder);
+
+            // There are two types of headers for Olympus MakerNotes
+            // See http://www.exiv2.org/makernote.html#R1
+            byte[] makerNoteHeader1Bytes = new byte[ORF_MAKER_NOTE_HEADER_1.length];
+            makerNoteDataInputStream.readFully(makerNoteHeader1Bytes);
+            makerNoteDataInputStream.seek(0);
+            byte[] makerNoteHeader2Bytes = new byte[ORF_MAKER_NOTE_HEADER_2.length];
+            makerNoteDataInputStream.readFully(makerNoteHeader2Bytes);
+            // Skip the corresponding amount of bytes for each header type
+            if (Arrays.equals(makerNoteHeader1Bytes, ORF_MAKER_NOTE_HEADER_1)) {
+                makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_1_SIZE);
+            } else if (Arrays.equals(makerNoteHeader2Bytes, ORF_MAKER_NOTE_HEADER_2)) {
+                makerNoteDataInputStream.seek(ORF_MAKER_NOTE_HEADER_2_SIZE);
+            }
+
+            // Read IFD data from MakerNote
+            readImageFileDirectory(makerNoteDataInputStream, ORF_MAKER_NOTE_HINT);
+
+            // Retrieve & update preview image offset & length values
+            ExifAttribute imageLengthAttribute = (ExifAttribute)
+                    mAttributes[ORF_CAMERA_SETTINGS_HINT].get(TAG_PREVIEW_IMAGE_START);
+            ExifAttribute bitsPerSampleAttribute = (ExifAttribute)
+                    mAttributes[ORF_CAMERA_SETTINGS_HINT].get(TAG_PREVIEW_IMAGE_LENGTH);
+
+            if (imageLengthAttribute != null && bitsPerSampleAttribute != null) {
+                mAttributes[IFD_PREVIEW_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT, imageLengthAttribute);
+                mAttributes[IFD_PREVIEW_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
+                        bitsPerSampleAttribute);
+            }
+
+            // TODO: Check this behavior in other ORF files
+            // Retrieve primary image length & width values
+            // See piex.cc GetOlympusPreviewImage()
+            ExifAttribute aspectFrameAttribute =
+                    (ExifAttribute) mAttributes[ORF_IMAGE_PROCESSING_HINT].get(TAG_ASPECT_FRAME);
+            if (aspectFrameAttribute != null) {
+                int[] aspectFrameValues = new int[4];
+                aspectFrameValues = (int[]) aspectFrameAttribute.getValue(mExifByteOrder);
+                if (aspectFrameValues[2] > aspectFrameValues[0] &&
+                        aspectFrameValues[3] > aspectFrameValues[1]) {
+                    int primaryImageWidth = aspectFrameValues[2] - aspectFrameValues[0] + 1;
+                    int primaryImageLength = aspectFrameValues[3] - aspectFrameValues[1] + 1;
+                    // Swap width & length values
+                    if (primaryImageWidth < primaryImageLength) {
+                        primaryImageWidth += primaryImageLength;
+                        primaryImageLength = primaryImageWidth - primaryImageLength;
+                        primaryImageWidth -= primaryImageLength;
+                    }
+                    ExifAttribute primaryImageWidthAttribute =
+                            ExifAttribute.createUShort(primaryImageWidth, mExifByteOrder);
+                    ExifAttribute primaryImageLengthAttribute =
+                            ExifAttribute.createUShort(primaryImageLength, mExifByteOrder);
+
+                    mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, primaryImageWidthAttribute);
+                    mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, primaryImageLengthAttribute);
+                }
+            }
+        }
+    }
+
     // Stores a new JPEG image with EXIF attributes into a given output stream.
     private void saveJpegAttributes(InputStream inputStream, OutputStream outputStream)
             throws IOException {
@@ -2308,8 +2508,7 @@
     }
 
     // Reads the given EXIF byte area and save its tag data into attributes.
-    private void readExifSegment(byte[] exifBytes, int exifOffsetFromBeginning, int imageType)
-            throws IOException {
+    private void readExifSegment(byte[] exifBytes, int imageType) throws IOException {
         ByteOrderAwarenessDataInputStream dataInputStream =
                 new ByteOrderAwarenessDataInputStream(exifBytes);
 
@@ -2318,9 +2517,6 @@
 
         // Read TIFF image file directories. See JEITA CP-3451C Section 4.5.2. Figure 6.
         readImageFileDirectory(dataInputStream, imageType);
-
-        // Process thumbnail.
-        processThumbnail(dataInputStream, exifOffsetFromBeginning, exifBytes.length);
     }
 
     private void addDefaultValuesForCompatibility() {
@@ -2350,37 +2546,41 @@
         }
     }
 
-    private void parseTiffHeaders(ByteOrderAwarenessDataInputStream dataInputStream,
-            int exifBytesLength) throws IOException {
-        // Read byte align
+    private ByteOrder readByteOrder(ByteOrderAwarenessDataInputStream dataInputStream)
+            throws IOException {
+        // Read byte order.
         short byteOrder = dataInputStream.readShort();
         switch (byteOrder) {
             case BYTE_ALIGN_II:
                 if (DEBUG) {
                     Log.d(TAG, "readExifSegment: Byte Align II");
                 }
-                mExifByteOrder = ByteOrder.LITTLE_ENDIAN;
-                break;
+                return ByteOrder.LITTLE_ENDIAN;
             case BYTE_ALIGN_MM:
                 if (DEBUG) {
                     Log.d(TAG, "readExifSegment: Byte Align MM");
                 }
-                mExifByteOrder = ByteOrder.BIG_ENDIAN;
-                break;
+                return ByteOrder.BIG_ENDIAN;
             default:
                 throw new IOException("Invalid byte order: " + Integer.toHexString(byteOrder));
         }
+    }
 
-        // Set byte order.
+    private void parseTiffHeaders(ByteOrderAwarenessDataInputStream dataInputStream,
+            int exifBytesLength) throws IOException {
+        // Read byte order
+        mExifByteOrder = readByteOrder(dataInputStream);
+        // Set byte order
         dataInputStream.setByteOrder(mExifByteOrder);
 
+        // Check start code
         int startCode = dataInputStream.readUnsignedShort();
-        if (startCode != START_CODE) {
-            throw new IOException("Invalid exif start code: " + Integer.toHexString(startCode));
+        if (mMimeType != IMAGE_TYPE_ORF && startCode != START_CODE) {
+            throw new IOException("Invalid start code: " + Integer.toHexString(startCode));
         }
 
-        // Read first ifd offset
-        long firstIfdOffset = dataInputStream.readUnsignedInt();
+        // Read and skip to first ifd offset
+        int firstIfdOffset = dataInputStream.readInt();
         if (firstIfdOffset < 8 || firstIfdOffset >= exifBytesLength) {
             throw new IOException("Invalid first Ifd offset: " + firstIfdOffset);
         }
@@ -2415,8 +2615,9 @@
             int tagNumber = dataInputStream.readUnsignedShort();
             int dataFormat = dataInputStream.readUnsignedShort();
             int numberOfComponents = dataInputStream.readInt();
-            long nextEntryOffset = dataInputStream.peek() + 4;  // next four bytes is for data
-                                                                // offset or value.
+            // Next four bytes is for data offset or value.
+            long nextEntryOffset = dataInputStream.peek() + 4;
+
             // Look up a corresponding tag from tag number
             final ExifTag tag = (ExifTag) sExifTagMapsForReading[hint].get(tagNumber);
 
@@ -2442,10 +2643,33 @@
             // field if the size of the entry value is bigger than 4.
             int byteCount = numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat];
             if (byteCount > 4) {
-                long offset = dataInputStream.readUnsignedInt();
+                int offset = dataInputStream.readInt();
                 if (DEBUG) {
                     Log.d(TAG, "seek to data offset: " + offset);
                 }
+                if (mMimeType == IMAGE_TYPE_ORF) {
+                    if (tag.name == TAG_MAKER_NOTE) {
+                        // Save offset value for reading thumbnail
+                        mOrfMakerNoteOffset = offset;
+                    } else if (hint == ORF_MAKER_NOTE_HINT && tag.name == TAG_THUMBNAIL_IMAGE) {
+                        // Retrieve & update values for thumbnail offset and length values for ORF
+                        mOrfThumbnailOffset = offset;
+                        mOrfThumbnailLength = numberOfComponents;
+
+                        ExifAttribute compressionAttribute =
+                                ExifAttribute.createUShort(DATA_JPEG, mExifByteOrder);
+                        ExifAttribute jpegInterchangeFormatAttribute =
+                                ExifAttribute.createULong(mOrfThumbnailOffset, mExifByteOrder);
+                        ExifAttribute jpegInterchangeFormatLengthAttribute =
+                                ExifAttribute.createULong(mOrfThumbnailLength, mExifByteOrder);
+
+                        mAttributes[IFD_THUMBNAIL_HINT].put(TAG_COMPRESSION, compressionAttribute);
+                        mAttributes[IFD_THUMBNAIL_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT,
+                                jpegInterchangeFormatAttribute);
+                        mAttributes[IFD_THUMBNAIL_HINT].put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
+                                jpegInterchangeFormatLengthAttribute);
+                    }
+                }
                 if (offset + byteCount <= dataInputStream.mLength) {
                     dataInputStream.seek(offset);
                 } else {
@@ -2478,7 +2702,8 @@
                         offset = dataInputStream.readUnsignedInt();
                         break;
                     }
-                    case IFD_FORMAT_SLONG: {
+                    case IFD_FORMAT_SLONG:
+                    case IFD_FORMAT_IFD: {
                         offset = dataInputStream.readInt();
                         break;
                     }
@@ -2501,7 +2726,7 @@
                 continue;
             }
 
-            byte[] bytes = new byte[numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat]];
+            byte[] bytes = new byte[byteCount];
             dataInputStream.readFully(bytes);
             mAttributes[hint].put(
                     tag.name, new ExifAttribute(dataFormat, numberOfComponents, bytes));
@@ -2511,7 +2736,7 @@
         }
 
         if (dataInputStream.peek() + 4 <= dataInputStream.mLength) {
-            long nextIfdOffset = dataInputStream.readUnsignedInt();
+            int nextIfdOffset = dataInputStream.readInt();
             if (DEBUG) {
                 Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset));
             }
@@ -2545,22 +2770,14 @@
                 int jpegInterchangeFormat =
                         jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder);
 
-                // Skip to the JPEG data offset
-                in.reset();
-                in.mark(in.available());
-                if (in.skip(jpegInterchangeFormat) != jpegInterchangeFormat) {
-                    Log.d(TAG, "Invalid JPEG offset");
-                }
-
                 // Searches for SOF marker in JPEG data and updates IMAGE_LENGTH & IMAGE_WIDTH tags
-                getJpegAttributes(in, imageType);
+                getJpegAttributes(in, jpegInterchangeFormat, imageType);
             }
         }
     }
 
-    // Processes thumbnail based on Compression Value
-    private void processThumbnail(ByteOrderAwarenessDataInputStream dataInputStream,
-            int exifOffsetFromBeginning, int exifBytesLength) throws IOException {
+    // Retrieves thumbnail based on Compression Value
+    private void setThumbnailData(InputStream in) throws IOException {
         HashMap thumbnailData = mAttributes[IFD_THUMBNAIL_HINT];
         ExifAttribute compressionAttribute = (ExifAttribute) thumbnailData.get(TAG_COMPRESSION);
         if (compressionAttribute != null) {
@@ -2582,9 +2799,8 @@
                                 jpegInterchangeFormatAttribute.getIntValue(mExifByteOrder);
                         int jpegInterchangeFormatLength =
                                 jpegInterchangeFormatLengthAttribute.getIntValue(mExifByteOrder);
-                        retrieveJPEGThumbnail(dataInputStream, jpegInterchangeFormat,
-                                jpegInterchangeFormatLength, exifOffsetFromBeginning,
-                                exifBytesLength);
+                        createJPEGThumbnailBitmap(in, jpegInterchangeFormat,
+                                jpegInterchangeFormatLength);
                     }
                     break;
                 }
@@ -2601,9 +2817,7 @@
                         if (stripOffsetsArray.length == 1) {
                             int stripOffsetsSum = (int) Arrays.stream(stripOffsetsArray).sum();
                             int stripByteCountsSum = (int) Arrays.stream(stripByteCountsArray).sum();
-                            retrieveJPEGThumbnail(dataInputStream, stripOffsetsSum,
-                                    stripByteCountsSum, exifOffsetFromBeginning,
-                                    exifBytesLength);
+                            createJPEGThumbnailBitmap(in, stripOffsetsSum, stripByteCountsSum);
                         } else {
                             // TODO: implement method to read multiple strips (b/29737797)
                             Log.d(TAG, "Multiple strip thumbnail data cannot be processed");
@@ -2619,27 +2833,29 @@
     }
 
     // Retrieves thumbnail for JPEG Compression
-    private void retrieveJPEGThumbnail(ByteOrderAwarenessDataInputStream dataInputStream,
-            int thumbnailOffset, int thumbnailLength, int exifOffsetFromBeginning,
-            int exifBytesLength) throws IOException {
+    private void createJPEGThumbnailBitmap(InputStream in, int thumbnailOffset, int thumbnailLength)
+            throws IOException {
         if (DEBUG) {
             Log.d(TAG, "Retrieving JPEG Thumbnail");
         }
         // The following code limits the size of thumbnail size not to overflow EXIF data area.
-        thumbnailLength = Math.min(thumbnailLength, exifBytesLength - thumbnailOffset);
-        // The following code changes the offset value for RAF files.
-        if (mMimeType == IMAGE_TYPE_RAF) {
-            exifOffsetFromBeginning += mRafJpegOffset;
+        thumbnailLength = Math.min(thumbnailLength, in.available() - thumbnailOffset);
+        if (mMimeType == IMAGE_TYPE_JPEG || mMimeType == IMAGE_TYPE_RAF) {
+            thumbnailOffset += mExifOffset;
+        } else if (mMimeType == IMAGE_TYPE_ORF) {
+            // Update offset value since RAF files have IFD data preceding MakerNote data.
+            thumbnailOffset += mOrfMakerNoteOffset;
         }
+        Log.d(TAG, "offset: " + thumbnailOffset);
         if (thumbnailOffset > 0 && thumbnailLength > 0) {
             mHasThumbnail = true;
-            mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset;
+            mThumbnailOffset = thumbnailOffset;
             mThumbnailLength = thumbnailLength;
             if (mFilename == null && mAssetInputStream == null && mSeekableFileDescriptor == null) {
                 // Save the thumbnail in memory if the input doesn't support reading again.
                 byte[] thumbnailBytes = new byte[thumbnailLength];
-                dataInputStream.seek(thumbnailOffset);
-                dataInputStream.readFully(thumbnailBytes);
+                in.skip(thumbnailOffset);
+                in.read(thumbnailBytes);
                 mThumbnailBytes = thumbnailBytes;
                 if (DEBUG) {
                     Bitmap bitmap = BitmapFactory.decodeByteArray(
@@ -2739,9 +2955,9 @@
 
     // Gets the corresponding IFD group index of the given tag number for writing Exif Tags.
     private static int getIfdHintFromTagNumber(int tagNumber) {
-        for (int i = 0; i < IFD_POINTER_TAG_HINTS.length; ++i) {
-            if (IFD_POINTER_TAGS[i].number == tagNumber) {
-                return IFD_POINTER_TAG_HINTS[i];
+        for (int i = 0; i < EXIF_POINTER_TAG_HINTS.length; ++i) {
+            if (EXIF_POINTER_TAGS[i].number == tagNumber) {
+                return EXIF_POINTER_TAG_HINTS[i];
             }
         }
         return -1;
@@ -2755,7 +2971,7 @@
         int[] ifdDataSizes = new int[EXIF_TAGS.length];
 
         // Remove IFD pointer tags (we'll re-add it later.)
-        for (ExifTag tag : IFD_POINTER_TAGS) {
+        for (ExifTag tag : EXIF_POINTER_TAGS) {
             removeAttribute(tag.name);
         }
         // Remove old thumbnail data
@@ -2775,15 +2991,15 @@
         // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD
         // offset when there is one or more tags in the thumbnail IFD.
         if (!mAttributes[IFD_EXIF_HINT].isEmpty()) {
-            mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name,
+            mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[1].name,
                     ExifAttribute.createULong(0, mExifByteOrder));
         }
         if (!mAttributes[IFD_GPS_HINT].isEmpty()) {
-            mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[2].name,
+            mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[2].name,
                     ExifAttribute.createULong(0, mExifByteOrder));
         }
         if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) {
-            mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[3].name,
+            mAttributes[IFD_EXIF_HINT].put(EXIF_POINTER_TAGS[3].name,
                     ExifAttribute.createULong(0, mExifByteOrder));
         }
         if (mHasThumbnail) {
@@ -2835,15 +3051,15 @@
 
         // Update IFD pointer tags with the calculated offsets.
         if (!mAttributes[IFD_EXIF_HINT].isEmpty()) {
-            mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name,
+            mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[1].name,
                     ExifAttribute.createULong(ifdOffsets[IFD_EXIF_HINT], mExifByteOrder));
         }
         if (!mAttributes[IFD_GPS_HINT].isEmpty()) {
-            mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[2].name,
+            mAttributes[IFD_TIFF_HINT].put(EXIF_POINTER_TAGS[2].name,
                     ExifAttribute.createULong(ifdOffsets[IFD_GPS_HINT], mExifByteOrder));
         }
         if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) {
-            mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[3].name, ExifAttribute.createULong(
+            mAttributes[IFD_EXIF_HINT].put(EXIF_POINTER_TAGS[3].name, ExifAttribute.createULong(
                     ifdOffsets[IFD_INTEROPERABILITY_HINT], mExifByteOrder));
         }
 
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 6f5199b..9e560d5 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -68,6 +68,9 @@
  * extractor.release();
  * extractor = null;
  * </pre>
+ *
+ * <p>This class requires the {@link android.Manifest.permission#INTERNET} permission
+ * when used with network-based content.
  */
 final public class MediaExtractor {
     public MediaExtractor() {
@@ -89,6 +92,10 @@
      *
      * @param context the Context to use when resolving the Uri
      * @param uri the Content URI of the data you want to extract from.
+     *
+     * <p>When <code>uri</code> refers to a network file the
+     * {@link android.Manifest.permission#INTERNET} permission is required.
+     *
      * @param headers the headers to be sent together with the request for the data.
      *        This can be {@code null} if no specific headers are to be sent with the
      *        request.
@@ -136,6 +143,10 @@
      * Sets the data source (file-path or http URL) to use.
      *
      * @param path the path of the file, or the http URL
+     *
+     * <p>When <code>path</code> refers to a network file the
+     * {@link android.Manifest.permission#INTERNET} permission is required.
+     *
      * @param headers the headers associated with the http request for the stream you want to play.
      *        This can be {@code null} if no specific headers are to be sent with the
      *        request.
@@ -181,6 +192,9 @@
      * directory), and that the pathname should reference a world-readable file.
      * As an alternative, the application could first open the file for reading,
      * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}.
+     *
+     * <p>When <code>path</code> refers to a network file the
+     * {@link android.Manifest.permission#INTERNET} permission is required.
      */
     public final void setDataSource(@NonNull String path) throws IOException {
         nativeSetDataSource(
diff --git a/media/jni/android_media_ExifInterface.cpp b/media/jni/android_media_ExifInterface.cpp
index 731deae..10b3170 100644
--- a/media/jni/android_media_ExifInterface.cpp
+++ b/media/jni/android_media_ExifInterface.cpp
@@ -130,7 +130,6 @@
     piex::PreviewImageData image_data;
 
     if (!GetExifFromRawImage(piexStream.get(), String8("[piex stream]"), image_data)) {
-        ALOGI("Raw image not detected");
         return NULL;
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Events.java b/packages/DocumentsUI/src/com/android/documentsui/Events.java
index 2d0dbe8..95934c3f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Events.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Events.java
@@ -35,34 +35,13 @@
      * Returns true if event was triggered by a mouse.
      */
     public static boolean isMouseEvent(MotionEvent e) {
-        return isMouseType(e.getToolType(0));
-    }
-
-    /**
-     * Returns true if event was triggered by a finger or stylus touch.
-     */
-    public static boolean isTouchEvent(MotionEvent e) {
-        return isTouchType(e.getToolType(0));
-    }
-
-    /**
-     * Returns true if event was triggered by a mouse.
-     */
-    public static boolean isMouseType(int toolType) {
+        int toolType = e.getToolType(0);
         return toolType == MotionEvent.TOOL_TYPE_MOUSE;
     }
 
     /**
      * Returns true if event was triggered by a finger or stylus touch.
      */
-    public static boolean isTouchType(int toolType) {
-        return toolType == MotionEvent.TOOL_TYPE_FINGER
-                || toolType == MotionEvent.TOOL_TYPE_STYLUS;
-    }
-
-    /**
-     * Returns true if event was triggered by a finger or stylus touch.
-     */
     public static boolean isActionDown(MotionEvent e) {
         return e.getActionMasked() == MotionEvent.ACTION_DOWN;
     }
@@ -116,7 +95,6 @@
      * of related code.
      */
     public interface InputEvent extends AutoCloseable {
-        boolean isTouchEvent();
         boolean isMouseEvent();
         boolean isPrimaryButtonPressed();
         boolean isSecondaryButtonPressed();
@@ -212,11 +190,6 @@
         }
 
         @Override
-        public boolean isTouchEvent() {
-            return Events.isTouchEvent(mEvent);
-        }
-
-        @Override
         public boolean isMouseEvent() {
             return Events.isMouseEvent(mEvent);
         }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/BandController.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/BandController.java
index 8f52036..eb53ec1 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/BandController.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/BandController.java
@@ -85,14 +85,14 @@
                 new RecyclerView.OnItemTouchListener() {
                     @Override
                     public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
-                        try (MotionInputEvent event = MotionInputEvent.obtain(e, view)) {
+                        try (InputEvent event = MotionInputEvent.obtain(e, view)) {
                             return handleEvent(event);
                         }
                     }
                     @Override
                     public void onTouchEvent(RecyclerView rv, MotionEvent e) {
                         if (Events.isMouseEvent(e)) {
-                            try (MotionInputEvent event = MotionInputEvent.obtain(e, view)) {
+                            try (InputEvent event = MotionInputEvent.obtain(e, view)) {
                                 processInputEvent(event);
                             }
                         }
@@ -177,7 +177,7 @@
         mSelection = selection;
     }
 
-    private boolean handleEvent(MotionInputEvent e) {
+    private boolean handleEvent(InputEvent e) {
         // Don't start, or extend bands on right click.
         if (e.isSecondaryButtonPressed()) {
             return false;
@@ -230,7 +230,7 @@
         }
     }
 
-    boolean shouldStart(MotionInputEvent e) {
+    boolean shouldStart(InputEvent e) {
         return !isActive()
                 && e.isActionDown()  // the initial button press
                 && mAdapter.getItemCount() > 0
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/TestInputEvent.java b/packages/DocumentsUI/tests/src/com/android/documentsui/TestInputEvent.java
index 36e7c1b..e6936d6 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/TestInputEvent.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/TestInputEvent.java
@@ -22,11 +22,6 @@
     }
 
     @Override
-    public boolean isTouchEvent() {
-        return !mouseEvent;
-    }
-
-    @Override
     public boolean isMouseEvent() {
         return mouseEvent;
     }
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 9061376..52565ba 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -30,7 +30,7 @@
     <color name="batterymeter_charge_color">#FFFFFFFF</color>
     <color name="batterymeter_bolt_color">#FFFFFFFF</color>
     <color name="qs_batterymeter_frame_color">#FF404040</color>
-    <color name="system_warning_color">#fff4511e</color><!-- deep orange 600 -->
+    <color name="system_warning_color">@*android:color/system_error</color>
     <color name="qs_text">#FFFFFFFF</color>
     <color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
     <color name="qs_subhead">#99FFFFFF</color><!-- 60% white -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 7b22b88..47ea59e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -190,6 +190,12 @@
 
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
+        if (mService.interceptMediaKey(event)) {
+            return true;
+        }
+        if (super.dispatchKeyEvent(event)) {
+            return true;
+        }
         boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
         switch (event.getKeyCode()) {
             case KeyEvent.KEYCODE_BACK:
@@ -214,10 +220,7 @@
                 }
                 break;
         }
-        if (mService.interceptMediaKey(event)) {
-            return true;
-        }
-        return super.dispatchKeyEvent(event);
+        return false;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 38dbaee..29577ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -366,7 +366,6 @@
      * @return true if a matching action was found, false otherwise
      */
     public boolean updatePendingIntentFromActions(Notification.Action[] actions) {
-        boolean found = false;
         if (mPendingIntent == null || actions == null) {
             return false;
         }
@@ -473,14 +472,21 @@
         }
 
         @Override
-        public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-            if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
-                defocusIfNeeded(true /* animate */);
-                final InputMethodManager imm = InputMethodManager.getInstance();
-                imm.hideSoftInputFromWindow(getWindowToken(), 0);
+        public boolean onKeyDown(int keyCode, KeyEvent event) {
+            if (keyCode == KeyEvent.KEYCODE_BACK) {
+                // Eat the DOWN event here to prevent any default behavior.
                 return true;
             }
-            return super.onKeyPreIme(keyCode, event);
+            return super.onKeyDown(keyCode, event);
+        }
+
+        @Override
+        public boolean onKeyUp(int keyCode, KeyEvent event) {
+            if (keyCode == KeyEvent.KEYCODE_BACK) {
+                defocusIfNeeded(true /* animate */);
+                return true;
+            }
+            return super.onKeyUp(keyCode, event);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3d9adbd..0c9a0bb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -21771,10 +21771,13 @@
         }
 
         @Override
-        public int startActivityAsPackage(String packageName, int userId, Intent intent,
+        public int startActivitiesAsPackage(String packageName, int userId, Intent[] intents,
                 Bundle bOptions) {
-            Preconditions.checkNotNull(intent, "intent");
-            final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver());
+            Preconditions.checkNotNull(intents, "intents");
+            final String[] resolvedTypes = new String[intents.length];
+            for (int i = 0; i < intents.length; i++) {
+                resolvedTypes[i] = intents[i].resolveTypeIfNeeded(mContext.getContentResolver());
+            }
 
             // UID of the package on user userId.
             // "= 0" is needed because otherwise catch(RemoteException) would make it look like
@@ -21788,9 +21791,8 @@
             }
 
             synchronized (ActivityManagerService.this) {
-                return startActivityInPackage(packageUid, packageName, intent, resolvedType,
-                        /*resultTo*/ null, /*resultWho*/ null, /*requestCode*/ 0, /*startFlags*/ 0,
-                        bOptions, userId, /*container*/ null, /*inTask*/ null);
+                return startActivitiesInPackage(packageUid, packageName, intents, resolvedTypes,
+                        /*resultTo*/ null, bOptions, userId);
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 1913b61..b4dd587 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -452,27 +452,28 @@
                 ensureShortcutPermission(callingPackage, userId);
             }
 
-            final Intent intent = mShortcutServiceInternal.createShortcutIntent(getCallingUserId(),
-                    callingPackage, packageName, shortcutId, userId);
-            if (intent == null) {
+            final Intent[] intents = mShortcutServiceInternal.createShortcutIntents(
+                    getCallingUserId(), callingPackage, packageName, shortcutId, userId);
+            if (intents == null || intents.length == 0) {
                 return false;
             }
             // Note the target activity doesn't have to be exported.
 
-            intent.setSourceBounds(sourceBounds);
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            // TODO Use sourceBounds
 
-            return startShortcutIntentAsPublisher(
-                    intent, packageName, startActivityOptions, userId);
+            intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            return startShortcutIntentsAsPublisher(
+                    intents, packageName, startActivityOptions, userId);
         }
 
-        private boolean startShortcutIntentAsPublisher(@NonNull Intent intent,
+        private boolean startShortcutIntentsAsPublisher(@NonNull Intent[] intents,
                 @NonNull String publisherPackage, Bundle startActivityOptions, int userId) {
             final int code;
             final long ident = injectClearCallingIdentity();
             try {
-                code = mActivityManagerInternal.startActivityAsPackage(publisherPackage,
-                        userId, intent, startActivityOptions);
+                code = mActivityManagerInternal.startActivitiesAsPackage(publisherPackage,
+                        userId, intents, startActivityOptions);
                 if (code >= ActivityManager.START_SUCCESS) {
                     return true; // Success
                 } else {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 7d57f33..51c9619 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -23,6 +23,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.ShortcutInfo;
 import android.content.res.Resources;
+import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.text.format.Formatter;
 import android.util.ArrayMap;
@@ -63,7 +64,8 @@
     private static final String TAG_VERIFY = ShortcutService.TAG + ".verify";
 
     static final String TAG_ROOT = "package";
-    private static final String TAG_INTENT_EXTRAS = "intent-extras";
+    private static final String TAG_INTENT_EXTRAS_LEGACY = "intent-extras";
+    private static final String TAG_INTENT = "intent";
     private static final String TAG_EXTRAS = "extras";
     private static final String TAG_SHORTCUT = "shortcut";
     private static final String TAG_CATEGORIES = "categories";
@@ -82,7 +84,8 @@
     private static final String ATTR_DISABLED_MESSAGE = "dmessage";
     private static final String ATTR_DISABLED_MESSAGE_RES_ID = "dmessageid";
     private static final String ATTR_DISABLED_MESSAGE_RES_NAME = "dmessagename";
-    private static final String ATTR_INTENT = "intent";
+    private static final String ATTR_INTENT_LEGACY = "intent";
+    private static final String ATTR_INTENT_NO_EXTRA = "intent-base";
     private static final String ATTR_RANK = "rank";
     private static final String ATTR_TIMESTAMP = "timestamp";
     private static final String ATTR_FLAGS = "flags";
@@ -1288,7 +1291,6 @@
                 si.getDisabledMessageResourceId());
         ShortcutService.writeAttr(out, ATTR_DISABLED_MESSAGE_RES_NAME,
                 si.getDisabledMessageResName());
-        ShortcutService.writeAttr(out, ATTR_INTENT, si.getIntentNoExtras());
         ShortcutService.writeAttr(out, ATTR_TIMESTAMP,
                 si.getLastChangedTimestamp());
         if (forBackup) {
@@ -1317,9 +1319,16 @@
                 out.endTag(null, TAG_CATEGORIES);
             }
         }
+        final Intent[] intentsNoExtras = si.getIntentsNoExtras();
+        final PersistableBundle[] intentsExtras = si.getIntentPersistableExtrases();
+        final int numIntents = intentsNoExtras.length;
+        for (int i = 0; i < numIntents; i++) {
+            out.startTag(null, TAG_INTENT);
+            ShortcutService.writeAttr(out, ATTR_INTENT_NO_EXTRA, intentsNoExtras[i]);
+            ShortcutService.writeTagExtra(out, TAG_EXTRAS, intentsExtras[i]);
+            out.endTag(null, TAG_INTENT);
+        }
 
-        ShortcutService.writeTagExtra(out, TAG_INTENT_EXTRAS,
-                si.getIntentPersistableExtras());
         ShortcutService.writeTagExtra(out, TAG_EXTRAS, si.getExtras());
 
         out.endTag(null, TAG_SHORTCUT);
@@ -1382,8 +1391,9 @@
         String disabledMessage;
         int disabledMessageResId;
         String disabledMessageResName;
-        Intent intent;
-        PersistableBundle intentPersistableExtras = null;
+        Intent intentLegacy;
+        PersistableBundle intentPersistableExtrasLegacy = null;
+        ArrayList<Intent> intents = new ArrayList<>();
         int rank;
         PersistableBundle extras = null;
         long lastChangedTimestamp;
@@ -1407,7 +1417,7 @@
                 ATTR_DISABLED_MESSAGE_RES_ID);
         disabledMessageResName = ShortcutService.parseStringAttribute(parser,
                 ATTR_DISABLED_MESSAGE_RES_NAME);
-        intent = ShortcutService.parseIntentAttribute(parser, ATTR_INTENT);
+        intentLegacy = ShortcutService.parseIntentAttributeNoDefault(parser, ATTR_INTENT_LEGACY);
         rank = (int) ShortcutService.parseLongAttribute(parser, ATTR_RANK);
         lastChangedTimestamp = ShortcutService.parseLongAttribute(parser, ATTR_TIMESTAMP);
         flags = (int) ShortcutService.parseLongAttribute(parser, ATTR_FLAGS);
@@ -1429,8 +1439,11 @@
                         depth, type, tag));
             }
             switch (tag) {
-                case TAG_INTENT_EXTRAS:
-                    intentPersistableExtras = PersistableBundle.restoreFromXml(parser);
+                case TAG_INTENT_EXTRAS_LEGACY:
+                    intentPersistableExtrasLegacy = PersistableBundle.restoreFromXml(parser);
+                    continue;
+                case TAG_INTENT:
+                    intents.add(parseIntent(parser));
                     continue;
                 case TAG_EXTRAS:
                     extras = PersistableBundle.restoreFromXml(parser);
@@ -1453,15 +1466,53 @@
             throw ShortcutService.throwForInvalidTag(depth, tag);
         }
 
+        if (intentLegacy != null) {
+            // For the legacy file format which supported only one intent per shortcut.
+            ShortcutInfo.setIntentExtras(intentLegacy, intentPersistableExtrasLegacy);
+            intents.clear();
+            intents.add(intentLegacy);
+        }
+
         return new ShortcutInfo(
                 userId, id, packageName, activityComponent, /* icon =*/ null,
                 title, titleResId, titleResName, text, textResId, textResName,
                 disabledMessage, disabledMessageResId, disabledMessageResName,
-                categories, intent,
-                intentPersistableExtras, rank, extras, lastChangedTimestamp, flags,
+                categories,
+                intents.toArray(new Intent[intents.size()]),
+                rank, extras, lastChangedTimestamp, flags,
                 iconResId, iconResName, bitmapPath);
     }
 
+    private static Intent parseIntent(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+
+        Intent intent = ShortcutService.parseIntentAttribute(parser,
+                ATTR_INTENT_NO_EXTRA);
+
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+            final int depth = parser.getDepth();
+            final String tag = parser.getName();
+            if (ShortcutService.DEBUG_LOAD) {
+                Slog.d(TAG, String.format("  depth=%d type=%d name=%s",
+                        depth, type, tag));
+            }
+            switch (tag) {
+                case TAG_EXTRAS:
+                    ShortcutInfo.setIntentExtras(intent,
+                            PersistableBundle.restoreFromXml(parser));
+                    continue;
+            }
+            throw ShortcutService.throwForInvalidTag(depth, tag);
+        }
+        return intent;
+    }
+
     @VisibleForTesting
     List<ShortcutInfo> getAllShortcutsForTest() {
         return new ArrayList<>(mShortcuts.values());
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index 7f5d931..e7b66fc 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -171,7 +171,7 @@
 
         final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
 
-        final long lastUpdateTime = ShortcutService.parseIntAttribute(
+        final long lastUpdateTime = ShortcutService.parseLongAttribute(
                 parser, ATTR_LAST_UPDATE_TIME);
 
         // When restoring from backup, it's always shadow.
diff --git a/services/core/java/com/android/server/pm/ShortcutParser.java b/services/core/java/com/android/server/pm/ShortcutParser.java
index 3f302d6..2a2a4b2 100644
--- a/services/core/java/com/android/server/pm/ShortcutParser.java
+++ b/services/core/java/com/android/server/pm/ShortcutParser.java
@@ -127,6 +127,7 @@
             ShortcutInfo currentShortcut = null;
 
             Set<String> categories = null;
+            final ArrayList<Intent> intents = new ArrayList<>();
 
             outer:
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -143,9 +144,15 @@
                     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 (si.isEnabled()) {
+                        if (intents.size() == 0) {
+                            Log.e(TAG, "Shortcut " + si.getId() + " has no intent. Skipping it.");
+                            continue;
+                        }
+                    } else {
+                        // Just set the default intent to disabled shortcuts.
+                        intents.clear();
+                        intents.add(new Intent(Intent.ACTION_VIEW));
                     }
 
                     if (numShortcuts >= maxShortcuts) {
@@ -153,6 +160,23 @@
                                 + activityInfo.getComponentName() + ". Skipping the rest.");
                         return result;
                     }
+
+                    // Same flag as what TaskStackBuilder adds.
+                    intents.get(0).addFlags(
+                            Intent.FLAG_ACTIVITY_NEW_TASK |
+                            Intent.FLAG_ACTIVITY_CLEAR_TASK |
+                            Intent.FLAG_ACTIVITY_TASK_ON_HOME);
+                    try {
+                        si.setIntents(intents.toArray(new Intent[intents.size()]));
+                    } 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;
+                    }
+                    intents.clear();
+
                     if (categories != null) {
                         si.setCategories(categories);
                         categories = null;
@@ -196,17 +220,12 @@
                             }
                         }
                     }
-                    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;
@@ -216,17 +235,10 @@
                             parser, attrs);
                     if (TextUtils.isEmpty(intent.getAction())) {
                         Log.e(TAG, "Shortcut intent action must be provided. activity=" + activity);
+                        currentShortcut = null; // Invalidate the current shortcut.
                         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;
-                    }
+                    intents.add(intent);
                     continue;
                 }
                 if (depth == 3 && TAG_CATEGORIES.equals(tag)) {
@@ -345,7 +357,6 @@
                 null, // disabled message res name
                 null, // categories
                 null, // intent
-                null, // intent extras
                 rank,
                 null, // extras
                 service.injectCurrentTimeMillis(),
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index a91e284..e6a9739 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -687,7 +687,7 @@
     }
 
     @Nullable
-    static Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
+    static Intent parseIntentAttributeNoDefault(XmlPullParser parser, String attribute) {
         final String value = parseStringAttribute(parser, attribute);
         Intent parsed = null;
         if (!TextUtils.isEmpty(value)) {
@@ -697,6 +697,12 @@
                 Slog.e(TAG, "Error parsing intent", e);
             }
         }
+        return parsed;
+    }
+
+    @Nullable
+    static Intent parseIntentAttribute(XmlPullParser parser, String attribute) {
+        Intent parsed = parseIntentAttributeNoDefault(parser, attribute);
         if (parsed == null) {
             // Default intent.
             parsed = new Intent(Intent.ACTION_VIEW);
@@ -2240,7 +2246,7 @@
         }
 
         @Override
-        public Intent createShortcutIntent(int launcherUserId,
+        public Intent[] createShortcutIntents(int launcherUserId,
                 @NonNull String callingPackage,
                 @NonNull String packageName, @NonNull String shortcutId, int userId) {
             // Calling permission must be checked by LauncherAppsImpl.
@@ -2259,7 +2265,7 @@
                     Log.e(TAG, "Shortcut " + shortcutId + " does not exist or disabled");
                     return null;
                 }
-                return si.getIntent();
+                return si.getIntents();
             }
         }
 
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 59070ba..4cbccca 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -487,6 +487,7 @@
     final SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
     final SparseArray<WallpaperData> mLockWallpaperMap = new SparseArray<WallpaperData>();
 
+    final SparseArray<Boolean> mUserRestorecon = new SparseArray<Boolean>();
     int mCurrentUserId;
 
     static class WallpaperData {
@@ -944,26 +945,32 @@
 
     void onUnlockUser(final int userId) {
         synchronized (mLock) {
-            if (mCurrentUserId == userId && mWaitingForUnlock) {
-                switchUser(userId, null);
+            if (mCurrentUserId == userId) {
+                if (mWaitingForUnlock) {
+                    // If we're switching users, now is when we transition the wallpaper
+                    switchUser(userId, null);
+                }
 
                 // Make sure that the SELinux labeling of all the relevant files is correct.
                 // This corrects for mislabeling bugs that might have arisen from move-to
                 // operations involving the wallpaper files.  This isn't timing-critical,
                 // so we do it in the background to avoid holding up the user unlock operation.
-                Runnable relabeler = new Runnable() {
-                    @Override
-                    public void run() {
-                        final File wallpaperDir = getWallpaperDir(userId);
-                        for (String filename : sPerUserFiles) {
-                            File f = new File(wallpaperDir, filename);
-                            if (f.exists()) {
-                                SELinux.restorecon(f);
+                if (mUserRestorecon.get(userId) != Boolean.TRUE) {
+                    mUserRestorecon.put(userId, Boolean.TRUE);
+                    Runnable relabeler = new Runnable() {
+                        @Override
+                        public void run() {
+                            final File wallpaperDir = getWallpaperDir(userId);
+                            for (String filename : sPerUserFiles) {
+                                File f = new File(wallpaperDir, filename);
+                                if (f.exists()) {
+                                    SELinux.restorecon(f);
+                                }
                             }
                         }
-                    }
-                };
-                BackgroundThread.getHandler().post(relabeler);
+                    };
+                    BackgroundThread.getHandler().post(relabeler);
+                }
             }
         }
     }
diff --git a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
index 3b63d93..1a60592 100644
--- a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
+++ b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
@@ -26,6 +26,7 @@
 import android.app.RetailDemoModeServiceInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -54,6 +55,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.CallLog;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.util.KeyValueListParser;
@@ -333,6 +335,8 @@
         um.setUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER, true, user);
         um.setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user);
         um.setUserRestriction(UserManager.DISALLOW_CONFIG_BLUETOOTH, true, user);
+        // Set this to false because the default is true on user creation
+        um.setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, false, user);
         // Disallow rebooting in safe mode - controlled by user 0
         getUserManager().setUserRestriction(UserManager.DISALLOW_SAFE_BOOT, true,
                 UserHandle.SYSTEM);
@@ -341,7 +345,8 @@
         Settings.Secure.putIntForUser(getContext().getContentResolver(),
                 Settings.Global.PACKAGE_VERIFIER_ENABLE, 0, userInfo.id);
 
-        grantRuntimePermissionToCamera(userInfo.getUserHandle());
+        grantRuntimePermissionToCamera(user);
+        clearPrimaryCallLog();
     }
 
     private void grantRuntimePermissionToCamera(UserHandle user) {
@@ -359,7 +364,18 @@
         } catch (Exception e) {
             // Ignore
         }
+    }
 
+    private void clearPrimaryCallLog() {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        // Deleting primary user call log so that it doesn't get copied to the new demo user
+        final Uri uri = CallLog.Calls.CONTENT_URI;
+        try {
+            resolver.delete(uri, null, null);
+        } catch (Exception e) {
+            Slog.w(TAG, "Deleting call log failed: " + e);
+        }
     }
 
     void logSessionDuration() {
diff --git a/services/tests/servicestests/res/xml/shortcut_error_4.xml b/services/tests/servicestests/res/xml/shortcut_error_4.xml
index 3697bb4..f680e99 100644
--- a/services/tests/servicestests/res/xml/shortcut_error_4.xml
+++ b/services/tests/servicestests/res/xml/shortcut_error_4.xml
@@ -19,7 +19,9 @@
         android:shortcutId="ms1"
         android:shortcutShortLabel="@string/shortcut_title1"
         >
-        <intent android:action="action1" />
+        <intent android:action="action1" >
+            <extra android:name="key1" android:value="value1" />
+        </intent>
     </shortcut>
 
     <!-- Invalid: no intent -->
@@ -28,13 +30,17 @@
         android:shortcutShortLabel="@string/shortcut_title1"
         />
 
-    <!-- Valid: more than one intent; first one will be picked. -->
+    <!-- Valid: more than one intent -->
     <shortcut
         android:shortcutId="ms2"
         android:shortcutShortLabel="@string/shortcut_title1"
         >
-        <intent android:action="action2_1" />
-        <intent android:action="action2_2" />
+        <intent android:action="action2_1" >
+            <extra android:name="key1" android:value="value1" />
+        </intent>
+        <intent android:action="action2_2">
+            <extra android:name="key2" android:value="value2" />
+        </intent>
     </shortcut>
 
     <!-- Valid: disabled shortcut doesn't need an intent -->
@@ -53,11 +59,12 @@
         <intent android:action="action4" />
     </shortcut>
 
-    <!-- Invalid, no intent action -->
+    <!-- Invalid, no intent action (if any of the intents is invalid, the entire shortcut will be invalid.) -->
     <shortcut
         android:shortcutId="ms_ignored2"
         android:shortcutShortLabel="@string/shortcut_title1"
         >
         <intent android:data="x"/>
+        <intent android:action="actionx"/>
     </shortcut>
 </shortcuts>
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 5864255..ff1c3b6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -34,14 +34,12 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
 import android.annotation.UserIdInt;
 import android.app.Activity;
-import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.IUidObserver;
 import android.app.usage.UsageStatsManagerInternal;
+import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -190,11 +188,6 @@
         }
 
         @Override
-        public void startActivityAsUser(@RequiresPermission Intent intent, @Nullable Bundle options,
-                UserHandle userId) {
-        }
-
-        @Override
         public int getUserId() {
             return UserHandle.USER_SYSTEM;
         }
@@ -706,17 +699,6 @@
         mUnlockedUsers.put(USER_11, true);
         mUnlockedUsers.put(USER_P0, true);
 
-        // Set up mMockActivityManagerInternal.
-        // By default, startActivityAsPackage() will simply forward to startActivityAsUser().
-        doAnswer(new AnswerWithSystemCheck<>(inv -> {
-            mServiceContext.startActivityAsUser(
-                    (Intent) inv.getArguments()[2],
-                    (Bundle) inv.getArguments()[3],
-                    UserHandle.of((Integer) inv.getArguments()[1]));
-            return ActivityManager.START_SUCCESS;
-        })).when(mMockActivityManagerInternal).startActivityAsPackage(anyString(), anyInt(),
-                any(Intent.class), any(Bundle.class));
-
         // Set up resources
         setUpAppResources();
 
@@ -1303,6 +1285,35 @@
         return s;
     }
 
+    protected ShortcutInfo makeShortcutWithIntents(String id, Intent... intents) {
+        return makeShortcut(
+                id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
+                intents, /* rank =*/ 0);
+    }
+
+    /**
+     * Make a shortcut with details.
+     */
+    protected ShortcutInfo makeShortcut(String id, String title, ComponentName activity,
+            Icon icon, Intent[] intents, int rank) {
+        final ShortcutInfo.Builder  b = new ShortcutInfo.Builder(mClientContext, id)
+                .setActivity(new ComponentName(mClientContext.getPackageName(), "dummy"))
+                .setShortLabel(title)
+                .setRank(rank)
+                .setIntents(intents);
+        if (icon != null) {
+            b.setIcon(icon);
+        }
+        if (activity != null) {
+            b.setActivity(activity);
+        }
+        final ShortcutInfo s = b.build();
+
+        s.setTimestamp(mInjectedCurrentTimeMillis); // HACK
+
+        return s;
+    }
+
     /**
      * Make a shortcut with details.
      */
@@ -1388,33 +1399,53 @@
         assertTrue(getPackageShortcut(packageName, shortcutId, userId) == null);
     }
 
+    protected Intent[] launchShortcutAndGetIntentsInner(Runnable shortcutStarter,
+            @NonNull String packageName, @NonNull String shortcutId, int userId) {
+        reset(mMockActivityManagerInternal);
+        shortcutStarter.run();
+
+        final ArgumentCaptor<Intent[]> intentsCaptor = ArgumentCaptor.forClass(Intent[].class);
+        verify(mMockActivityManagerInternal).startActivitiesAsPackage(
+                eq(packageName),
+                eq(userId),
+                intentsCaptor.capture(),
+                any(Bundle.class));
+        return intentsCaptor.getValue();
+    }
+
+    protected Intent[] launchShortcutAndGetIntents(
+            @NonNull String packageName, @NonNull String shortcutId, int userId) {
+        return launchShortcutAndGetIntentsInner(
+                () -> {
+                    mLauncherApps.startShortcut(packageName, shortcutId, null, null,
+                            UserHandle.of(userId));
+                }, packageName, shortcutId, userId
+        );
+    }
+
     protected Intent launchShortcutAndGetIntent(
             @NonNull String packageName, @NonNull String shortcutId, int userId) {
-        reset(mServiceContext);
-        mLauncherApps.startShortcut(packageName, shortcutId, null, null,
-                UserHandle.of(userId));
+        final Intent[] intents = launchShortcutAndGetIntents(packageName, shortcutId, userId);
+        assertEquals(1, intents.length);
+        return intents[0];
+    }
 
-        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mServiceContext).startActivityAsUser(
-                intentCaptor.capture(),
-                any(Bundle.class),
-                eq(UserHandle.of(userId)));
-        return intentCaptor.getValue();
+    protected Intent[] launchShortcutAndGetIntents_withShortcutInfo(
+            @NonNull String packageName, @NonNull String shortcutId, int userId) {
+        return launchShortcutAndGetIntentsInner(
+                () -> {
+                    mLauncherApps.startShortcut(
+                            getShortcutInfoAsLauncher(packageName, shortcutId, userId), null, null);
+                }, packageName, shortcutId, userId
+        );
     }
 
     protected Intent launchShortcutAndGetIntent_withShortcutInfo(
             @NonNull String packageName, @NonNull String shortcutId, int userId) {
-        reset(mServiceContext);
-
-        mLauncherApps.startShortcut(
-                getShortcutInfoAsLauncher(packageName, shortcutId, userId), null, null);
-
-        final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mServiceContext).startActivityAsUser(
-                intentCaptor.capture(),
-                any(Bundle.class),
-                eq(UserHandle.of(userId)));
-        return intentCaptor.getValue();
+        final Intent[] intents = launchShortcutAndGetIntents_withShortcutInfo(
+                packageName, shortcutId, userId);
+        assertEquals(1, intents.length);
+        return intents[0];
     }
 
     protected void assertShortcutLaunchable(@NonNull String packageName, @NonNull String shortcutId,
@@ -1423,9 +1454,25 @@
         assertNotNull(launchShortcutAndGetIntent_withShortcutInfo(packageName, shortcutId, userId));
     }
 
-    protected void assertShortcutNotLaunchable(@NonNull String packageName,
+    protected void assertShortcutNotLaunched(@NonNull String packageName,
+            @NonNull String shortcutId, int userId) {
+        reset(mMockActivityManagerInternal);
+        try {
+            mLauncherApps.startShortcut(packageName, shortcutId, null, null,
+                    UserHandle.of(userId));
+            fail("ActivityNotFoundException was not thrown");
+        } catch (ActivityNotFoundException expected) {
+        }
+        // This shouldn't have been called.
+        verify(mMockActivityManagerInternal, times(0)).startActivitiesAsPackage(
+                anyString(),
+                anyInt(),
+                any(Intent[].class),
+                any(Bundle.class));
+    }
+
+    protected void assertStartShortcutThrowsException(@NonNull String packageName,
             @NonNull String shortcutId, int userId, Class<?> expectedException) {
-        reset(mServiceContext);
         Exception thrown = null;
         try {
             mLauncherApps.startShortcut(packageName, shortcutId, null, null,
@@ -1433,11 +1480,6 @@
         } catch (Exception e) {
             thrown = e;
         }
-        // This shouldn't have been called.
-        verify(mServiceContext, times(0)).startActivityAsUser(
-                any(Intent.class),
-                any(Bundle.class),
-                any(UserHandle.class));
         assertNotNull("Exception was not thrown", thrown);
         assertEquals("Exception type different", expectedException, thrown.getClass());
     }
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 55fa625f..5206507 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -58,10 +58,12 @@
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.Manifest.permission;
 import android.app.ActivityManager;
@@ -1681,7 +1683,7 @@
                     .areAllPinned()
                     .areAllNotWithKeyFieldsOnly()
                     .areAllDisabled();
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_0,
                     ActivityNotFoundException.class);
 
             // Here, s4 is still enabled and launchable, but s3 is disabled.
@@ -1698,7 +1700,7 @@
                     .selectByIds("s4")
                     .areAllEnabled();
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s3", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_2, "s3", USER_0,
                     ActivityNotFoundException.class);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s4", USER_0);
 
@@ -2130,17 +2132,17 @@
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
                     SecurityException.class);
         });
         runWithCaller(LAUNCHER_1, USER_P0, () -> {
@@ -2178,17 +2180,17 @@
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
                     SecurityException.class);
         });
         runWithCaller(LAUNCHER_2, USER_P0, () -> {
@@ -2226,17 +2228,17 @@
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
                     SecurityException.class);
         });
         runWithCaller(LAUNCHER_2, USER_10, () -> {
@@ -2297,26 +2299,26 @@
                     "s1", "s2", "s3");
 
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_0,
                     ActivityNotFoundException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_0,
                     ActivityNotFoundException.class);
 
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
                     SecurityException.class);
         });
         runWithCaller(LAUNCHER_1, USER_P0, () -> {
@@ -2348,24 +2350,24 @@
 
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_0,
                     ActivityNotFoundException.class);
 
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
                     SecurityException.class);
         });
         runWithCaller(LAUNCHER_2, USER_P0, () -> {
@@ -2396,26 +2398,26 @@
                     "s1", "s3");
 
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_0,
                     ActivityNotFoundException.class);
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
 
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s2", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_2, "s2", USER_0,
                     ActivityNotFoundException.class);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
                     SecurityException.class);
         });
         runWithCaller(LAUNCHER_2, USER_10, () -> {
@@ -2432,28 +2434,28 @@
                     /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_10)),
                     "s1", "s2", "s3");
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_0,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_0,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_0,
                     SecurityException.class);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s1", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_2, "s1", USER_0,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s2", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_2, "s2", USER_0,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s3", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_2, "s3", USER_0,
                     SecurityException.class);
 
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
                     ActivityNotFoundException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
                     ActivityNotFoundException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
                     ActivityNotFoundException.class);
         });
 
@@ -2490,26 +2492,26 @@
                     "s1", "s2", "s3");
 
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_0,
                     ActivityNotFoundException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_0,
                     ActivityNotFoundException.class);
 
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
                     SecurityException.class);
         });
         runWithCaller(LAUNCHER_1, USER_P0, () -> {
@@ -2541,24 +2543,24 @@
 
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_0,
                     ActivityNotFoundException.class);
 
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
                     SecurityException.class);
         });
         runWithCaller(LAUNCHER_2, USER_P0, () -> {
@@ -2589,26 +2591,26 @@
                     "s1", "s3");
 
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_0,
                     ActivityNotFoundException.class);
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
 
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s2", USER_0,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_2, "s2", USER_0,
                     ActivityNotFoundException.class);
             assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
 
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s2", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s3", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s4", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s5", USER_10,
                     SecurityException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10,
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s6", USER_10,
                     SecurityException.class);
         });
     }
@@ -2680,10 +2682,8 @@
 
             assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
 
-            assertShortcutNotLaunchable("no-such-package", "s2", USER_0,
-                    ActivityNotFoundException.class);
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "xxxx", USER_0,
-                    ActivityNotFoundException.class);
+            assertShortcutNotLaunched("no-such-package", "s2", USER_0);
+            assertShortcutNotLaunched(CALLING_PACKAGE_1, "xxxx", USER_0);
         });
 
         // LAUNCHER_1 is no longer the default launcher
@@ -2710,19 +2710,18 @@
         // Test inner errors.
         runWithCaller(LAUNCHER_1, USER_0, () -> {
             // Not launchable.
-            doAnswer(new AnswerWithSystemCheck<>(inv -> {
-                return ActivityManager.START_CLASS_NOT_FOUND;
-            })).when(mMockActivityManagerInternal).startActivityAsPackage(anyString(), anyInt(),
-                    any(Intent.class), any(Bundle.class));
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_0,
+            doReturn(ActivityManager.START_CLASS_NOT_FOUND)
+                    .when(mMockActivityManagerInternal).startActivitiesAsPackage(
+                            anyString(), anyInt(), any(Intent[].class), any(Bundle.class));
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_0,
                     ActivityNotFoundException.class);
 
             // Still not launchable.
-            doAnswer(new AnswerWithSystemCheck<>(inv -> {
-                return ActivityManager.START_PERMISSION_DENIED;
-            })).when(mMockActivityManagerInternal).startActivityAsPackage(anyString(), anyInt(),
-                    any(Intent.class), any(Bundle.class));
-            assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_0,
+            doReturn(ActivityManager.START_CLASS_NOT_FOUND)
+                    .when(mMockActivityManagerInternal)
+                    .startActivitiesAsPackage(
+                            anyString(), anyInt(), any(Intent[].class), any(Bundle.class));
+            assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_0,
                     ActivityNotFoundException.class);
         });
 
@@ -5924,11 +5923,41 @@
                     .areAllEnabled()
                     .forShortcutWithId("ms1", si -> {
                         assertTrue(si.isEnabled());
+                        assertEquals(1, si.getIntents().length);
+
                         assertEquals("action1", si.getIntent().getAction());
+                        assertEquals("value1", si.getIntent().getStringExtra("key1"));
+                        assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK |
+                                Intent.FLAG_ACTIVITY_CLEAR_TASK |
+                                Intent.FLAG_ACTIVITY_TASK_ON_HOME, si.getIntent().getFlags());
+
+                        assertEquals("action1", si.getIntents()[0].getAction());
+                        assertEquals("value1", si.getIntents()[0].getStringExtra("key1"));
+                        assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK |
+                                Intent.FLAG_ACTIVITY_CLEAR_TASK |
+                                Intent.FLAG_ACTIVITY_TASK_ON_HOME, si.getIntents()[0].getFlags());
                     })
                     .forShortcutWithId("ms2", si -> {
                         assertTrue(si.isEnabled());
-                        assertEquals("action2_1", si.getIntent().getAction());
+                        assertEquals(2, si.getIntents().length);
+
+                        // getIntent will return the last one.
+                        assertEquals("action2_2", si.getIntent().getAction());
+                        assertEquals("value2", si.getIntent().getStringExtra("key2"));
+                        assertEquals(0, si.getIntent().getFlags());
+
+                        final Intent i1 = si.getIntents()[0];
+                        final Intent i2 = si.getIntents()[1];
+
+                        assertEquals("action2_1", i1.getAction());
+                        assertEquals("value1", i1.getStringExtra("key1"));
+                        assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK |
+                                        Intent.FLAG_ACTIVITY_CLEAR_TASK |
+                                        Intent.FLAG_ACTIVITY_TASK_ON_HOME, i1.getFlags());
+
+                        assertEquals("action2_2", i2.getAction());
+                        assertEquals("value2", i2.getStringExtra("key2"));
+                        assertEquals(0, i2.getFlags());
                     });
         });
 
@@ -6001,7 +6030,8 @@
                         assertEquals(si.getId(), "action1", si.getIntent().getAction());
                     })
                     .forShortcutWithId("ms2", si -> {
-                        assertEquals(si.getId(), "action2_1", si.getIntent().getAction());
+                        // getIntent returns the last one.
+                        assertEquals(si.getId(), "action2_2", si.getIntent().getAction());
                     })
                     .forShortcutWithId("ms3", si -> {
                         assertEquals(si.getId(), Intent.ACTION_VIEW, si.getIntent().getAction());
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
index efd380b..f97355a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -90,7 +90,7 @@
 
         assertExpectException(
                 RuntimeException.class,
-                "intent cannot be null",
+                "intents cannot contain null",
                 () -> new ShortcutInfo.Builder(getTestContext(), "id").setIntent(null));
 
         assertExpectException(
@@ -879,11 +879,16 @@
         final long now = mInjectedCurrentTimeMillis;
         mInjectedCurrentTimeMillis += 1;
 
+        dumpsysOnLogcat("before save");
+
         // Save and load.
         mService.saveDirtyInfo();
         initService();
         mService.handleUnlockUser(USER_10);
 
+        dumpUserFile(USER_10);
+        dumpsysOnLogcat("after load");
+
         ShortcutInfo si;
         si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id", USER_10);
 
@@ -912,6 +917,8 @@
         // to test it.
         si = mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "id2", USER_10);
         assertEquals(1, si.getRank());
+
+        dumpUserFile(USER_10);
     }
 
     public void testShortcutInfoSaveAndLoad_resId() throws InterruptedException {
@@ -1133,7 +1140,30 @@
                     assertEquals(intent.getAction(), si.getIntent().getAction());
                     assertEquals(intent.getData(), si.getIntent().getData());
                     assertEquals(intent.getComponent(), si.getIntent().getComponent());
-                    assertBundlesEqual(intent.getExtras(), si.getExtras());
+                    assertBundlesEqual(intent.getExtras(), si.getIntent().getExtras());
+                });
+    }
+
+    private void checkShortcutInfoSaveAndLoad_intents(Intent... intents) {
+        assertTrue(mManager.setDynamicShortcuts(list(
+                makeShortcutWithIntents("s1", intents))));
+        initService();
+        mService.handleUnlockUser(USER_0);
+
+        assertWith(getCallerShortcuts())
+                .haveIds("s1")
+                .forShortcutWithId("s1", si -> {
+
+                    final Intent[] actual = si.getIntents();
+                    assertEquals(intents.length, actual.length);
+
+                    for (int i = 0; i < intents.length; i++) {
+                        assertEquals(intents[i].getAction(), actual[i].getAction());
+                        assertEquals(intents[i].getData(), actual[i].getData());
+                        assertEquals(intents[i].getComponent(), actual[i].getComponent());
+                        assertEquals(intents[i].getFlags(), actual[i].getFlags());
+                        assertBundlesEqual(intents[i].getExtras(), actual[i].getExtras());
+                    }
                 });
     }
 
@@ -1176,6 +1206,30 @@
                 .putExtras(makeBundle("a", "b")));
 
         mInjectedCurrentTimeMillis += INTERVAL; // reset throttling.
+
+        // Multi-intents
+        checkShortcutInfoSaveAndLoad_intents(
+                new Intent(Intent.ACTION_MAIN).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK),
+                new Intent(Intent.ACTION_VIEW).setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
+        );
+
+        checkShortcutInfoSaveAndLoad_intents(
+                new Intent(Intent.ACTION_MAIN).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+                        .setComponent(new ComponentName("a", "b")),
+                new Intent(Intent.ACTION_VIEW)
+                        .setComponent(new ComponentName("a", "b"))
+                );
+
+        checkShortcutInfoSaveAndLoad_intents(
+                new Intent(Intent.ACTION_MAIN)
+                        .setComponent(new ComponentName("a", "b")),
+                new Intent(Intent.ACTION_VIEW).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+                        .setComponent(new ComponentName("a", "b")),
+                new Intent("xyz").setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
+                        | Intent.FILL_IN_COMPONENT)
+                        .setComponent(new ComponentName("a", "b")).putExtras(
+                        makeBundle("xx", "yy"))
+                );
     }
 
     public void testThrottling() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest4.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest4.java
index 54c4b22..583c3d4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest4.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest4.java
@@ -100,7 +100,7 @@
     );
 
     public void testPersistingWeirdCharacters() {
-        final Intent intent = new Intent(Intent.ACTION_VIEW)
+        final Intent intent = new Intent(Intent.ACTION_MAIN)
                 .putExtras(sIntentExtras);
 
         runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
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 2f64ad7..ce7adfa 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
@@ -861,8 +861,8 @@
         if (b1 == null && b2 == null) {
             return; // pass
         }
-        assertNotNull(b1);
-        assertNotNull(b2);
+        assertNotNull("b1 is null but b2 is not", b1);
+        assertNotNull("b2 is null but b1 is not", b2);
 
         // HashSet makes the error message readable.
         assertEquals(set(b1.keySet()), set(b2.keySet()));
@@ -973,7 +973,8 @@
 
         waitOnMainThread();
 
-        launcherApps.unregisterCallback(asserter.getMockCallback());
+        // TODO unregister doesn't work well during unit tests.  Figure out and fix it.
+        // launcherApps.unregisterCallback(asserter.getMockCallback());
 
         return asserter;
     }
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 3779d87..e89585c 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -696,8 +696,13 @@
         Slog.w(TAG, "Recognition aborted");
         MetricsLogger.count(mContext, "sth_recognition_aborted", 1);
         ModelData modelData = getModelDataForLocked(event.soundModelHandle);
-        if (modelData != null) {
+        if (modelData != null && modelData.isModelStarted()) {
             modelData.setStopped();
+            try {
+                modelData.getCallback().onRecognitionPaused();
+            } catch (RemoteException e) {
+                Slog.w(TAG, "RemoteException in onRecognitionPaused", e);
+            }
         }
     }
 
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index a7878d1..b278831 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -2929,6 +2929,19 @@
         if (!keepTag && inApplication && depth == 3) {
             if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
                 keepTag = true;
+
+                if (mainDex) {
+                    String8 componentProcess = AaptXml::getAttribute(tree,
+                            "http://schemas.android.com/apk/res/android", "process", &error);
+                    if (error != "") {
+                        fprintf(stderr, "ERROR: %s\n", error.string());
+                        return -1;
+                    }
+
+                    const String8& process =
+                            componentProcess.length() > 0 ? componentProcess : defaultProcess;
+                    keepTag = process.length() > 0 && process.find(":") != 0;
+                }
             }
         }
         if (keepTag) {
@@ -2941,19 +2954,6 @@
 
             keepTag = name.length() > 0;
 
-            if (keepTag && mainDex) {
-                String8 componentProcess = AaptXml::getAttribute(tree,
-                        "http://schemas.android.com/apk/res/android", "process", &error);
-                if (error != "") {
-                    fprintf(stderr, "ERROR: %s\n", error.string());
-                    return -1;
-                }
-
-                const String8& process =
-                        componentProcess.length() > 0 ? componentProcess : defaultProcess;
-                keepTag = process.length() > 0 && process.find(":") != 0;
-            }
-
             if (keepTag) {
                 addProguardKeepRule(keep, name, pkg.string(),
                         assFile->getPrintableSource(), tree.getLineNumber());
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index 9061660..902ec4c 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -170,16 +170,10 @@
                     }
                 }
             } else if (node->name == "activity" || node->name == "service" ||
-                    node->name == "receiver" || node->name == "provider" ||
-                    node->name == "instrumentation") {
+                    node->name == "receiver" || node->name == "provider") {
                 getName = true;
-            }
 
-            if (getName) {
-                xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, "name");
-                getName = attr != nullptr;
-
-                if (getName && mMainDexOnly) {
+                if (mMainDexOnly) {
                     xml::Attribute* componentProcess = node->findAttribute(xml::kSchemaAndroid,
                                                                            "process");
 
@@ -187,6 +181,13 @@
                             : mDefaultProcess;
                     getName = !process.empty() && process[0] != ':';
                 }
+            } else if (node-> name == "instrumentation") {
+                getName = true;
+            }
+
+            if (getName) {
+                xml::Attribute* attr = node->findAttribute(xml::kSchemaAndroid, "name");
+                getName = attr != nullptr;
 
                 if (getName) {
                     Maybe<std::string> result = util::getFullyQualifiedClassName(mPackage,
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
index efa6211..b8dd1a5 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
@@ -49,8 +49,8 @@
     // session API
     void updatePublish(int clientId, int sessionId, in PublishConfig publishConfig);
     void updateSubscribe(int clientId, int sessionId, in SubscribeConfig subscribeConfig);
-    void sendMessage(int clientId, int sessionId, int peerId, in byte[] message, int messageLength,
-            int messageId, int retryCount);
+    void sendMessage(int clientId, int sessionId, int peerId, in byte[] message, int messageId,
+        int retryCount);
     void terminateSession(int clientId, int sessionId);
     int startRanging(int clientId, int sessionId, in RttManager.ParcelableRttParams parms);
 }
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl b/wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl
index 7162be7..ff2c409 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl
+++ b/wifi/java/android/net/wifi/nan/IWifiNanSessionCallback.aidl
@@ -28,10 +28,9 @@
     void onSessionConfigFail(int reason);
     void onSessionTerminated(int reason);
 
-    void onMatch(int peerId, in byte[] serviceSpecificInfo,
-            int serviceSpecificInfoLength, in byte[] matchFilter, int matchFilterLength);
+    void onMatch(int peerId, in byte[] serviceSpecificInfo, in byte[] matchFilter);
 
     void onMessageSendSuccess(int messageId);
     void onMessageSendFail(int messageId, int reason);
-    void onMessageReceived(int peerId, in byte[] message, int messageLength);
+    void onMessageReceived(int peerId, in byte[] message);
 }
diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.java b/wifi/java/android/net/wifi/nan/PublishConfig.java
index a4969bd..3bc5251 100644
--- a/wifi/java/android/net/wifi/nan/PublishConfig.java
+++ b/wifi/java/android/net/wifi/nan/PublishConfig.java
@@ -18,9 +18,12 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import libcore.util.HexEncoding;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.charset.StandardCharsets;
@@ -64,31 +67,16 @@
     /**
      * @hide
      */
-    public final int mServiceSpecificInfoLength;
-
-    /**
-     * @hide
-     */
     public final byte[] mServiceSpecificInfo;
 
     /**
      * @hide
      */
-    public final int mTxFilterLength;
-
-    /**
-     * @hide
-     */
     public final byte[] mTxFilter;
 
     /**
      * @hide
      */
-    public final int mRxFilterLength;
-
-    /**
-     * @hide
-     */
     public final byte[] mRxFilter;
 
     /**
@@ -112,15 +100,11 @@
     public final boolean mEnableTerminateNotification;
 
     private PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo,
-            int serviceSpecificInfoLength, byte[] txFilter, int txFilterLength, byte[] rxFilter,
-            int rxFilterLength, int publishType, int publichCount, int ttlSec,
+            byte[] txFilter, byte[] rxFilter, int publishType, int publichCount, int ttlSec,
             boolean enableTerminateNotification) {
         mServiceName = serviceName;
-        mServiceSpecificInfoLength = serviceSpecificInfoLength;
         mServiceSpecificInfo = serviceSpecificInfo;
-        mTxFilterLength = txFilterLength;
         mTxFilter = txFilter;
-        mRxFilterLength = rxFilterLength;
         mRxFilter = rxFilter;
         mPublishType = publishType;
         mPublishCount = publichCount;
@@ -130,12 +114,10 @@
 
     @Override
     public String toString() {
-        return "PublishConfig [mServiceName='" + mServiceName + "', mServiceSpecificInfo='"
-                + (new String(mServiceSpecificInfo, 0, mServiceSpecificInfoLength))
-                + "', mTxFilter="
-                + (new TlvBufferUtils.TlvIterable(0, 1, mTxFilter, mTxFilterLength)).toString()
-                + ", mRxFilter="
-                + (new TlvBufferUtils.TlvIterable(0, 1, mRxFilter, mRxFilterLength)).toString()
+        return "PublishConfig [mServiceName='" + mServiceName + ", mServiceSpecificInfo='" + (
+                (mServiceSpecificInfo == null) ? "null" : HexEncoding.encode(mServiceSpecificInfo))
+                + ", mTxFilter=" + (new TlvBufferUtils.TlvIterable(0, 1, mTxFilter)).toString()
+                + ", mRxFilter=" + (new TlvBufferUtils.TlvIterable(0, 1, mRxFilter)).toString()
                 + ", mPublishType=" + mPublishType + ", mPublishCount=" + mPublishCount
                 + ", mTtlSec=" + mTtlSec + ", mEnableTerminateNotification="
                 + mEnableTerminateNotification + "]";
@@ -148,22 +130,10 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mServiceName.length);
-        if (mServiceName.length != 0) {
-            dest.writeByteArray(mServiceName);
-        }
-        dest.writeInt(mServiceSpecificInfoLength);
-        if (mServiceSpecificInfoLength != 0) {
-            dest.writeByteArray(mServiceSpecificInfo, 0, mServiceSpecificInfoLength);
-        }
-        dest.writeInt(mTxFilterLength);
-        if (mTxFilterLength != 0) {
-            dest.writeByteArray(mTxFilter, 0, mTxFilterLength);
-        }
-        dest.writeInt(mRxFilterLength);
-        if (mRxFilterLength != 0) {
-            dest.writeByteArray(mRxFilter, 0, mRxFilterLength);
-        }
+        dest.writeByteArray(mServiceName);
+        dest.writeByteArray(mServiceSpecificInfo);
+        dest.writeByteArray(mTxFilter);
+        dest.writeByteArray(mRxFilter);
         dest.writeInt(mPublishType);
         dest.writeInt(mPublishCount);
         dest.writeInt(mTtlSec);
@@ -178,34 +148,17 @@
 
         @Override
         public PublishConfig createFromParcel(Parcel in) {
-            int serviceNameLength = in.readInt();
-            byte[] serviceName = new byte[serviceNameLength];
-            if (serviceNameLength != 0) {
-                in.readByteArray(serviceName);
-            }
-            int ssiLength = in.readInt();
-            byte[] ssi = new byte[ssiLength];
-            if (ssiLength != 0) {
-                in.readByteArray(ssi);
-            }
-            int txFilterLength = in.readInt();
-            byte[] txFilter = new byte[txFilterLength];
-            if (txFilterLength != 0) {
-                in.readByteArray(txFilter);
-            }
-            int rxFilterLength = in.readInt();
-            byte[] rxFilter = new byte[rxFilterLength];
-            if (rxFilterLength != 0) {
-                in.readByteArray(rxFilter);
-            }
+            byte[] serviceName = in.createByteArray();
+            byte[] ssi = in.createByteArray();
+            byte[] txFilter = in.createByteArray();
+            byte[] rxFilter = in.createByteArray();
             int publishType = in.readInt();
             int publishCount = in.readInt();
             int ttlSec = in.readInt();
             boolean enableTerminateNotification = in.readInt() != 0;
 
-            return new PublishConfig(serviceName, ssi, ssiLength, txFilter, txFilterLength,
-                    rxFilter, rxFilterLength, publishType, publishCount, ttlSec,
-                    enableTerminateNotification);
+            return new PublishConfig(serviceName, ssi, txFilter, rxFilter, publishType,
+                    publishCount, ttlSec, enableTerminateNotification);
         }
     };
 
@@ -221,45 +174,10 @@
 
         PublishConfig lhs = (PublishConfig) o;
 
-        if (!Arrays.equals(mServiceName, lhs.mServiceName)
-                || mServiceSpecificInfoLength != lhs.mServiceSpecificInfoLength
-                || mTxFilterLength != lhs.mTxFilterLength
-                || mRxFilterLength != lhs.mRxFilterLength) {
-            return false;
-        }
-
-        if (mServiceSpecificInfo != null && lhs.mServiceSpecificInfo != null) {
-            for (int i = 0; i < mServiceSpecificInfoLength; ++i) {
-                if (mServiceSpecificInfo[i] != lhs.mServiceSpecificInfo[i]) {
-                    return false;
-                }
-            }
-        } else if (mServiceSpecificInfoLength != 0) {
-            return false; // invalid != invalid
-        }
-
-        if (mTxFilter != null && lhs.mTxFilter != null) {
-            for (int i = 0; i < mTxFilterLength; ++i) {
-                if (mTxFilter[i] != lhs.mTxFilter[i]) {
-                    return false;
-                }
-            }
-        } else if (mTxFilterLength != 0) {
-            return false; // invalid != invalid
-        }
-
-        if (mRxFilter != null && lhs.mRxFilter != null) {
-            for (int i = 0; i < mRxFilterLength; ++i) {
-                if (mRxFilter[i] != lhs.mRxFilter[i]) {
-                    return false;
-                }
-            }
-        } else if (mRxFilterLength != 0) {
-            return false; // invalid != invalid
-        }
-
-        return mPublishType == lhs.mPublishType && mPublishCount == lhs.mPublishCount
-                && mTtlSec == lhs.mTtlSec
+        return Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(mServiceSpecificInfo,
+                lhs.mServiceSpecificInfo) && Arrays.equals(mTxFilter, lhs.mTxFilter)
+                && Arrays.equals(mRxFilter, lhs.mRxFilter) && mPublishType == lhs.mPublishType
+                && mPublishCount == lhs.mPublishCount && mTtlSec == lhs.mTtlSec
                 && mEnableTerminateNotification == lhs.mEnableTerminateNotification;
     }
 
@@ -268,11 +186,8 @@
         int result = 17;
 
         result = 31 * result + Arrays.hashCode(mServiceName);
-        result = 31 * result + mServiceSpecificInfoLength;
         result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
-        result = 31 * result + mTxFilterLength;
         result = 31 * result + Arrays.hashCode(mTxFilter);
-        result = 31 * result + mRxFilterLength;
         result = 31 * result + Arrays.hashCode(mRxFilter);
         result = 31 * result + mPublishType;
         result = 31 * result + mPublishCount;
@@ -291,24 +206,11 @@
     public void validate() throws IllegalArgumentException {
         WifiNanUtils.validateServiceName(mServiceName);
 
-        if (mServiceSpecificInfoLength != 0 && (mServiceSpecificInfo == null
-                || mServiceSpecificInfo.length < mServiceSpecificInfoLength)) {
-            throw new IllegalArgumentException("Non-matching combination of "
-                    + "serviceSpecificInfo and serviceSpecificInfoLength");
-        }
-        if (mTxFilterLength != 0 && (mTxFilter == null || mTxFilter.length < mTxFilterLength)) {
-            throw new IllegalArgumentException(
-                    "Non-matching combination of txFilter and txFilterLength");
-        }
-        if (!TlvBufferUtils.isValid(mTxFilter, mTxFilterLength, 0, 1)) {
+        if (!TlvBufferUtils.isValid(mTxFilter, 0, 1)) {
             throw new IllegalArgumentException(
                     "Invalid txFilter configuration - LV fields do not match up to length");
         }
-        if (mRxFilterLength != 0 && (mRxFilter == null || mRxFilter.length < mRxFilterLength)) {
-            throw new IllegalArgumentException(
-                    "Non-matching combination of rxFilter and rxFilterLength");
-        }
-        if (!TlvBufferUtils.isValid(mRxFilter, mRxFilterLength, 0, 1)) {
+        if (!TlvBufferUtils.isValid(mRxFilter, 0, 1)) {
             throw new IllegalArgumentException(
                     "Invalid rxFilter configuration - LV fields do not match up to length");
         }
@@ -321,11 +223,13 @@
         if (mTtlSec < 0) {
             throw new IllegalArgumentException("Invalid ttlSec - must be non-negative");
         }
-        if (mPublishType == PublishConfig.PUBLISH_TYPE_UNSOLICITED && mRxFilterLength != 0) {
+        if (mPublishType == PublishConfig.PUBLISH_TYPE_UNSOLICITED && mRxFilter != null
+                && mRxFilter.length != 0) {
             throw new IllegalArgumentException("Invalid publish config: UNSOLICITED "
                     + "publishes (active) can't have an Rx filter");
         }
-        if (mPublishType == PublishConfig.PUBLISH_TYPE_SOLICITED && mTxFilterLength != 0) {
+        if (mPublishType == PublishConfig.PUBLISH_TYPE_SOLICITED && mTxFilter != null
+                && mTxFilter.length != 0) {
             throw new IllegalArgumentException("Invalid publish config: SOLICITED "
                     + "publishes (passive) can't have a Tx filter");
         }
@@ -336,12 +240,9 @@
      */
     public static final class Builder {
         private byte[] mServiceName;
-        private int mServiceSpecificInfoLength;
-        private byte[] mServiceSpecificInfo = new byte[0];
-        private int mTxFilterLength;
-        private byte[] mTxFilter = new byte[0];
-        private int mRxFilterLength;
-        private byte[] mRxFilter = new byte[0];
+        private byte[] mServiceSpecificInfo;
+        private byte[] mTxFilter;
+        private byte[] mRxFilter;
         private int mPublishType = PUBLISH_TYPE_UNSOLICITED;
         private int mPublishCount = 0;
         private int mTtlSec = 0;
@@ -377,26 +278,17 @@
          *
          * @param serviceSpecificInfo A byte-array for the service-specific
          *            information field.
-         * @param serviceSpecificInfoLength The length of the byte-array to be
-         *            used.
          * @return The builder to facilitate chaining
          *         {@code builder.setXXX(..).setXXX(..)}.
          */
-        public Builder setServiceSpecificInfo(byte[] serviceSpecificInfo,
-                int serviceSpecificInfoLength) {
-            if (serviceSpecificInfoLength != 0 && (serviceSpecificInfo == null
-                    || serviceSpecificInfo.length < serviceSpecificInfoLength)) {
-                throw new IllegalArgumentException("Non-matching combination of "
-                        + "serviceSpecificInfo and serviceSpecificInfoLength");
-            }
-            mServiceSpecificInfoLength = serviceSpecificInfoLength;
+        public Builder setServiceSpecificInfo(@Nullable byte[] serviceSpecificInfo) {
             mServiceSpecificInfo = serviceSpecificInfo;
             return this;
         }
 
         /**
          * Specify service specific information for the publish session - same
-         * as {@link PublishConfig.Builder#setServiceSpecificInfo(byte[], int)}
+         * as {@link PublishConfig.Builder#setServiceSpecificInfo(byte[])}
          * but obtaining the data from a String.
          *
          * @param serviceSpecificInfoStr The service specific information string
@@ -407,7 +299,6 @@
          */
         public Builder setServiceSpecificInfo(@NonNull String serviceSpecificInfoStr) {
             mServiceSpecificInfo = serviceSpecificInfoStr.getBytes();
-            mServiceSpecificInfoLength = mServiceSpecificInfo.length;
             return this;
         }
 
@@ -424,18 +315,11 @@
          *
          * @param txFilter The byte-array containing the LV formatted transmit
          *            filter.
-         * @param txFilterLength The number of bytes in the transmit filter
-         *            argument.
          * @return The builder to facilitate chaining
          *         {@code builder.setXXX(..).setXXX(..)}.
          */
-        public Builder setTxFilter(byte[] txFilter, int txFilterLength) {
-            if (txFilterLength != 0 && (txFilter == null || txFilter.length < txFilterLength)) {
-                throw new IllegalArgumentException(
-                        "Non-matching combination of txFilter and txFilterLength");
-            }
+        public Builder setTxFilter(@Nullable byte[] txFilter) {
             mTxFilter = txFilter;
-            mTxFilterLength = txFilterLength;
             return this;
         }
 
@@ -452,18 +336,11 @@
          *
          * @param rxFilter The byte-array containing the LV formatted receive
          *            filter.
-         * @param rxFilterLength The number of bytes in the receive filter
-         *            argument.
          * @return The builder to facilitate chaining
          *         {@code builder.setXXX(..).setXXX(..)}.
          */
-        public Builder setRxFilter(byte[] rxFilter, int rxFilterLength) {
-            if (rxFilterLength != 0 && (rxFilter == null || rxFilter.length < rxFilterLength)) {
-                throw new IllegalArgumentException(
-                        "Non-matching combination of rxFilter and rxFilterLength");
-            }
+        public Builder setRxFilter(@Nullable byte[] rxFilter) {
             mRxFilter = rxFilter;
-            mRxFilterLength = rxFilterLength;
             return this;
         }
 
@@ -547,9 +424,8 @@
          * builder.
          */
         public PublishConfig build() {
-            return new PublishConfig(mServiceName, mServiceSpecificInfo, mServiceSpecificInfoLength,
-                    mTxFilter, mTxFilterLength, mRxFilter, mRxFilterLength, mPublishType,
-                    mPublishCount, mTtlSec, mEnableTerminateNotification);
+            return new PublishConfig(mServiceName, mServiceSpecificInfo, mTxFilter, mRxFilter,
+                    mPublishType, mPublishCount, mTtlSec, mEnableTerminateNotification);
         }
     }
 }
diff --git a/wifi/java/android/net/wifi/nan/SubscribeConfig.java b/wifi/java/android/net/wifi/nan/SubscribeConfig.java
index 0d3ea01..4e53073 100644
--- a/wifi/java/android/net/wifi/nan/SubscribeConfig.java
+++ b/wifi/java/android/net/wifi/nan/SubscribeConfig.java
@@ -18,9 +18,12 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import libcore.util.HexEncoding;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.charset.StandardCharsets;
@@ -82,31 +85,16 @@
     /**
      * @hide
      */
-    public final int mServiceSpecificInfoLength;
-
-    /**
-     * @hide
-     */
     public final byte[] mServiceSpecificInfo;
 
     /**
      * @hide
      */
-    public final int mTxFilterLength;
-
-    /**
-     * @hide
-     */
     public final byte[] mTxFilter;
 
     /**
      * @hide
      */
-    public final int mRxFilterLength;
-
-    /**
-     * @hide
-     */
     public final byte[] mRxFilter;
 
     /**
@@ -134,16 +122,12 @@
      */
     public final boolean mEnableTerminateNotification;
 
-    private SubscribeConfig(byte[] serviceName, byte[] serviceSpecificInfo,
-            int serviceSpecificInfoLength, byte[] txFilter, int txFilterLength, byte[] rxFilter,
-            int rxFilterLength, int subscribeType, int publichCount, int ttlSec, int matchStyle,
+    private SubscribeConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] txFilter,
+            byte[] rxFilter, int subscribeType, int publichCount, int ttlSec, int matchStyle,
             boolean enableTerminateNotification) {
         mServiceName = serviceName;
-        mServiceSpecificInfoLength = serviceSpecificInfoLength;
         mServiceSpecificInfo = serviceSpecificInfo;
-        mTxFilterLength = txFilterLength;
         mTxFilter = txFilter;
-        mRxFilterLength = rxFilterLength;
         mRxFilter = rxFilter;
         mSubscribeType = subscribeType;
         mSubscribeCount = publichCount;
@@ -154,12 +138,10 @@
 
     @Override
     public String toString() {
-        return "SubscribeConfig [mServiceName='" + mServiceName + "', mServiceSpecificInfo='"
-                + (new String(mServiceSpecificInfo, 0, mServiceSpecificInfoLength))
-                + "', mTxFilter="
-                + (new TlvBufferUtils.TlvIterable(0, 1, mTxFilter, mTxFilterLength)).toString()
-                + ", mRxFilter="
-                + (new TlvBufferUtils.TlvIterable(0, 1, mRxFilter, mRxFilterLength)).toString()
+        return "SubscribeConfig [mServiceName='" + mServiceName + ", mServiceSpecificInfo='" + (
+                (mServiceSpecificInfo == null) ? "null" : HexEncoding.encode(mServiceSpecificInfo))
+                + ", mTxFilter=" + (new TlvBufferUtils.TlvIterable(0, 1, mTxFilter)).toString()
+                + ", mRxFilter=" + (new TlvBufferUtils.TlvIterable(0, 1, mRxFilter)).toString()
                 + ", mSubscribeType=" + mSubscribeType + ", mSubscribeCount=" + mSubscribeCount
                 + ", mTtlSec=" + mTtlSec + ", mMatchType=" + mMatchStyle
                 + ", mEnableTerminateNotification=" + mEnableTerminateNotification + "]";
@@ -172,22 +154,10 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mServiceName.length);
-        if (mServiceName.length != 0) {
-            dest.writeByteArray(mServiceName);
-        }
-        dest.writeInt(mServiceSpecificInfoLength);
-        if (mServiceSpecificInfoLength != 0) {
-            dest.writeByteArray(mServiceSpecificInfo, 0, mServiceSpecificInfoLength);
-        }
-        dest.writeInt(mTxFilterLength);
-        if (mTxFilterLength != 0) {
-            dest.writeByteArray(mTxFilter, 0, mTxFilterLength);
-        }
-        dest.writeInt(mRxFilterLength);
-        if (mRxFilterLength != 0) {
-            dest.writeByteArray(mRxFilter, 0, mRxFilterLength);
-        }
+        dest.writeByteArray(mServiceName);
+        dest.writeByteArray(mServiceSpecificInfo);
+        dest.writeByteArray(mTxFilter);
+        dest.writeByteArray(mRxFilter);
         dest.writeInt(mSubscribeType);
         dest.writeInt(mSubscribeCount);
         dest.writeInt(mTtlSec);
@@ -203,35 +173,18 @@
 
         @Override
         public SubscribeConfig createFromParcel(Parcel in) {
-            int serviceNameLength = in.readInt();
-            byte[] serviceName = new byte[serviceNameLength];
-            if (serviceNameLength != 0) {
-                in.readByteArray(serviceName);
-            }
-            int ssiLength = in.readInt();
-            byte[] ssi = new byte[ssiLength];
-            if (ssiLength != 0) {
-                in.readByteArray(ssi);
-            }
-            int txFilterLength = in.readInt();
-            byte[] txFilter = new byte[txFilterLength];
-            if (txFilterLength != 0) {
-                in.readByteArray(txFilter);
-            }
-            int rxFilterLength = in.readInt();
-            byte[] rxFilter = new byte[rxFilterLength];
-            if (rxFilterLength != 0) {
-                in.readByteArray(rxFilter);
-            }
+            byte[] serviceName = in.createByteArray();
+            byte[] ssi = in.createByteArray();
+            byte[] txFilter = in.createByteArray();
+            byte[] rxFilter = in.createByteArray();
             int subscribeType = in.readInt();
             int subscribeCount = in.readInt();
             int ttlSec = in.readInt();
             int matchStyle = in.readInt();
             boolean enableTerminateNotification = in.readInt() != 0;
 
-            return new SubscribeConfig(serviceName, ssi, ssiLength, txFilter, txFilterLength,
-                    rxFilter, rxFilterLength, subscribeType, subscribeCount, ttlSec, matchStyle,
-                    enableTerminateNotification);
+            return new SubscribeConfig(serviceName, ssi, txFilter, rxFilter, subscribeType,
+                    subscribeCount, ttlSec, matchStyle, enableTerminateNotification);
         }
     };
 
@@ -247,45 +200,11 @@
 
         SubscribeConfig lhs = (SubscribeConfig) o;
 
-        if (!Arrays.equals(mServiceName, lhs.mServiceName)
-                || mServiceSpecificInfoLength != lhs.mServiceSpecificInfoLength
-                || mTxFilterLength != lhs.mTxFilterLength
-                || mRxFilterLength != lhs.mRxFilterLength) {
-            return false;
-        }
-
-        if (mServiceSpecificInfo != null && lhs.mServiceSpecificInfo != null) {
-            for (int i = 0; i < mServiceSpecificInfoLength; ++i) {
-                if (mServiceSpecificInfo[i] != lhs.mServiceSpecificInfo[i]) {
-                    return false;
-                }
-            }
-        } else if (mServiceSpecificInfoLength != 0) {
-            return false; // invalid != invalid
-        }
-
-        if (mTxFilter != null && lhs.mTxFilter != null) {
-            for (int i = 0; i < mTxFilterLength; ++i) {
-                if (mTxFilter[i] != lhs.mTxFilter[i]) {
-                    return false;
-                }
-            }
-        } else if (mTxFilterLength != 0) {
-            return false; // invalid != invalid
-        }
-
-        if (mRxFilter != null && lhs.mRxFilter != null) {
-            for (int i = 0; i < mRxFilterLength; ++i) {
-                if (mRxFilter[i] != lhs.mRxFilter[i]) {
-                    return false;
-                }
-            }
-        } else if (mRxFilterLength != 0) {
-            return false; // invalid != invalid
-        }
-
-        return mSubscribeType == lhs.mSubscribeType && mSubscribeCount == lhs.mSubscribeCount
-                && mTtlSec == lhs.mTtlSec && mMatchStyle == lhs.mMatchStyle
+        return Arrays.equals(mServiceName, lhs.mServiceName) && Arrays.equals(mServiceSpecificInfo,
+                lhs.mServiceSpecificInfo) && Arrays.equals(mTxFilter, lhs.mTxFilter)
+                && Arrays.equals(mRxFilter, lhs.mRxFilter) && mSubscribeType == lhs.mSubscribeType
+                && mSubscribeCount == lhs.mSubscribeCount && mTtlSec == lhs.mTtlSec
+                && mMatchStyle == lhs.mMatchStyle
                 && mEnableTerminateNotification == lhs.mEnableTerminateNotification;
     }
 
@@ -294,11 +213,8 @@
         int result = 17;
 
         result = 31 * result + Arrays.hashCode(mServiceName);
-        result = 31 * result + mServiceSpecificInfoLength;
         result = 31 * result + Arrays.hashCode(mServiceSpecificInfo);
-        result = 31 * result + mTxFilterLength;
         result = 31 * result + Arrays.hashCode(mTxFilter);
-        result = 31 * result + mRxFilterLength;
         result = 31 * result + Arrays.hashCode(mRxFilter);
         result = 31 * result + mSubscribeType;
         result = 31 * result + mSubscribeCount;
@@ -318,24 +234,11 @@
     public void validate() throws IllegalArgumentException {
         WifiNanUtils.validateServiceName(mServiceName);
 
-        if (mServiceSpecificInfoLength != 0 && (mServiceSpecificInfo == null
-                || mServiceSpecificInfo.length < mServiceSpecificInfoLength)) {
-            throw new IllegalArgumentException("Non-matching combination of "
-                    + "serviceSpecificInfo and serviceSpecificInfoLength");
-        }
-        if (mTxFilterLength != 0 && (mTxFilter == null || mTxFilter.length < mTxFilterLength)) {
-            throw new IllegalArgumentException(
-                    "Non-matching combination of txFilter and txFilterLength");
-        }
-        if (!TlvBufferUtils.isValid(mTxFilter, mTxFilterLength, 0, 1)) {
+        if (!TlvBufferUtils.isValid(mTxFilter, 0, 1)) {
             throw new IllegalArgumentException(
                     "Invalid txFilter configuration - LV fields do not match up to length");
         }
-        if (mRxFilterLength != 0 && (mRxFilter == null || mRxFilter.length < mRxFilterLength)) {
-            throw new IllegalArgumentException(
-                    "Non-matching combination of rxFilter and rxFilterLength");
-        }
-        if (!TlvBufferUtils.isValid(mRxFilter, mRxFilterLength, 0, 1)) {
+        if (!TlvBufferUtils.isValid(mRxFilter, 0, 1)) {
             throw new IllegalArgumentException(
                     "Invalid rxFilter configuration - LV fields do not match up to length");
         }
@@ -352,11 +255,13 @@
             throw new IllegalArgumentException(
                     "Invalid matchType - must be MATCH_FIRST_ONLY or MATCH_ALL");
         }
-        if (mSubscribeType == SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE && mRxFilterLength != 0) {
+        if (mSubscribeType == SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE && mRxFilter != null
+                && mRxFilter.length != 0) {
             throw new IllegalArgumentException(
                     "Invalid subscribe config: ACTIVE subscribes can't have an Rx filter");
         }
-        if (mSubscribeType == SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE && mTxFilterLength != 0) {
+        if (mSubscribeType == SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE && mTxFilter != null
+                && mTxFilter.length != 0) {
             throw new IllegalArgumentException(
                     "Invalid subscribe config: PASSIVE subscribes can't have a Tx filter");
         }
@@ -367,12 +272,9 @@
      */
     public static final class Builder {
         private byte[] mServiceName;
-        private int mServiceSpecificInfoLength;
-        private byte[] mServiceSpecificInfo = new byte[0];
-        private int mTxFilterLength;
-        private byte[] mTxFilter = new byte[0];
-        private int mRxFilterLength;
-        private byte[] mRxFilter = new byte[0];
+        private byte[] mServiceSpecificInfo;
+        private byte[] mTxFilter;
+        private byte[] mRxFilter;
         private int mSubscribeType = SUBSCRIBE_TYPE_PASSIVE;
         private int mSubscribeCount = 0;
         private int mTtlSec = 0;
@@ -409,19 +311,10 @@
          *
          * @param serviceSpecificInfo A byte-array for the service-specific
          *            information field.
-         * @param serviceSpecificInfoLength The length of the byte-array to be
-         *            used.
          * @return The builder to facilitate chaining
          *         {@code builder.setXXX(..).setXXX(..)}.
          */
-        public Builder setServiceSpecificInfo(byte[] serviceSpecificInfo,
-                int serviceSpecificInfoLength) {
-            if (serviceSpecificInfoLength != 0 && (serviceSpecificInfo == null
-                    || serviceSpecificInfo.length < serviceSpecificInfoLength)) {
-                throw new IllegalArgumentException("Non-matching combination of "
-                        + "serviceSpecificInfo and serviceSpecificInfoLength");
-            }
-            mServiceSpecificInfoLength = serviceSpecificInfoLength;
+        public Builder setServiceSpecificInfo(@Nullable byte[] serviceSpecificInfo) {
             mServiceSpecificInfo = serviceSpecificInfo;
             return this;
         }
@@ -429,7 +322,7 @@
         /**
          * Specify service specific information for the subscribe session - same
          * as
-         * {@link SubscribeConfig.Builder#setServiceSpecificInfo(byte[], int)}
+         * {@link SubscribeConfig.Builder#setServiceSpecificInfo(byte[])}
          * but obtaining the data from a String.
          *
          * @param serviceSpecificInfoStr The service specific information string
@@ -440,7 +333,6 @@
          */
         public Builder setServiceSpecificInfo(@NonNull String serviceSpecificInfoStr) {
             mServiceSpecificInfo = serviceSpecificInfoStr.getBytes();
-            mServiceSpecificInfoLength = mServiceSpecificInfo.length;
             return this;
         }
 
@@ -457,18 +349,11 @@
          *
          * @param txFilter The byte-array containing the LV formatted transmit
          *            filter.
-         * @param txFilterLength The number of bytes in the transmit filter
-         *            argument.
          * @return The builder to facilitate chaining
          *         {@code builder.setXXX(..).setXXX(..)}.
          */
-        public Builder setTxFilter(byte[] txFilter, int txFilterLength) {
-            if (txFilterLength != 0 && (txFilter == null || txFilter.length < txFilterLength)) {
-                throw new IllegalArgumentException(
-                        "Non-matching combination of txFilter and txFilterLength");
-            }
+        public Builder setTxFilter(@Nullable byte[] txFilter) {
             mTxFilter = txFilter;
-            mTxFilterLength = txFilterLength;
             return this;
         }
 
@@ -484,18 +369,11 @@
          *
          * @param rxFilter The byte-array containing the LV formatted receive
          *            filter.
-         * @param rxFilterLength The number of bytes in the receive filter
-         *            argument.
          * @return The builder to facilitate chaining
          *         {@code builder.setXXX(..).setXXX(..)}.
          */
-        public Builder setRxFilter(byte[] rxFilter, int rxFilterLength) {
-            if (rxFilterLength != 0 && (rxFilter == null || rxFilter.length < rxFilterLength)) {
-                throw new IllegalArgumentException(
-                        "Non-matching combination of rxFilter and rxFilterLength");
-            }
+        public Builder setRxFilter(@Nullable byte[] rxFilter) {
             mRxFilter = rxFilter;
-            mRxFilterLength = rxFilterLength;
             return this;
         }
 
@@ -563,7 +441,7 @@
          * Sets the match style of the subscription - how are matches from a
          * single match session (corresponding to the same publish action on the
          * peer) reported to the host (using the
-         * {@link WifiNanSessionCallback#onMatch(int, byte[], int, byte[], int)}
+         * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])}
          * ). The options are: only report the first match and ignore the rest
          * {@link SubscribeConfig#MATCH_STYLE_FIRST_ONLY} or report every single
          * match {@link SubscribeConfig#MATCH_STYLE_ALL}.
@@ -601,9 +479,8 @@
          * builder.
          */
         public SubscribeConfig build() {
-            return new SubscribeConfig(mServiceName, mServiceSpecificInfo,
-                    mServiceSpecificInfoLength, mTxFilter, mTxFilterLength, mRxFilter,
-                    mRxFilterLength, mSubscribeType, mSubscribeCount, mTtlSec, mMatchStyle,
+            return new SubscribeConfig(mServiceName, mServiceSpecificInfo, mTxFilter, mRxFilter,
+                    mSubscribeType, mSubscribeCount, mTtlSec, mMatchStyle,
                     mEnableTerminateNotification);
         }
     }
diff --git a/wifi/java/android/net/wifi/nan/TlvBufferUtils.java b/wifi/java/android/net/wifi/nan/TlvBufferUtils.java
index da7ecd8..8ad9fa3 100644
--- a/wifi/java/android/net/wifi/nan/TlvBufferUtils.java
+++ b/wifi/java/android/net/wifi/nan/TlvBufferUtils.java
@@ -16,10 +16,13 @@
 
 package android.net.wifi.nan;
 
+import android.annotation.Nullable;
+
 import libcore.io.Memory;
 
 import java.nio.BufferOverflowException;
 import java.nio.ByteOrder;
+import java.util.Arrays;
 import java.util.Iterator;
 
 /**
@@ -50,8 +53,7 @@
      * Values are added to the structure using the {@code TlvConstructor.put*()}
      * methods.
      * <p>
-     * The final byte array is obtained using {@link TlvConstructor#getArray()}
-     * and {@link TlvConstructor#getActualLength()} methods.
+     * The final byte array is obtained using {@link TlvConstructor#getArray()}.
      */
     public static class TlvConstructor {
         private int mTypeSize;
@@ -88,9 +90,9 @@
          * @return The constructor to facilitate chaining
          *         {@code ctr.putXXX(..).putXXX(..)}.
          */
-        public TlvConstructor wrap(byte[] array) {
+        public TlvConstructor wrap(@Nullable byte[] array) {
             mArray = array;
-            mArrayLength = array.length;
+            mArrayLength = (array == null) ? 0 : array.length;
             return this;
         }
 
@@ -137,10 +139,13 @@
          * @return The constructor to facilitate chaining
          *         {@code ctr.putXXX(..).putXXX(..)}.
          */
-        public TlvConstructor putByteArray(int type, byte[] array, int offset, int length) {
+        public TlvConstructor putByteArray(int type, @Nullable byte[] array, int offset,
+                int length) {
             checkLength(length);
             addHeader(type, length);
-            System.arraycopy(array, offset, mArray, mPosition, length);
+            if (length != 0) {
+                System.arraycopy(array, offset, mArray, mPosition, length);
+            }
             mPosition += length;
             return this;
         }
@@ -155,8 +160,8 @@
          * @return The constructor to facilitate chaining
          *         {@code ctr.putXXX(..).putXXX(..)}.
          */
-        public TlvConstructor putByteArray(int type, byte[] array) {
-            return putByteArray(type, array, 0, array.length);
+        public TlvConstructor putByteArray(int type, @Nullable byte[] array) {
+            return putByteArray(type, array, 0, (array == null) ? 0 : array.length);
         }
 
         /**
@@ -223,24 +228,25 @@
          * @return The constructor to facilitate chaining
          *         {@code ctr.putXXX(..).putXXX(..)}.
          */
-        public TlvConstructor putString(int type, String data) {
-            byte[] bytes = data.getBytes();
-            return putByteArray(type, bytes, 0, bytes.length);
+        public TlvConstructor putString(int type, @Nullable String data) {
+            byte[] bytes = null;
+            int length = 0;
+            if (data != null) {
+                bytes = data.getBytes();
+                length = bytes.length;
+            }
+            return putByteArray(type, bytes, 0, length);
         }
 
         /**
-         * Returns the constructed TLV formatted byte-array. Note that the
-         * returned array is the fully wrapped (
-         * {@link TlvConstructor#wrap(byte[])}) or allocated (
-         * {@link TlvConstructor#allocate(int)}) array - which isn't necessarily
-         * the actual size of the formatted data. Use
-         * {@link TlvConstructor#getActualLength()} to obtain the size of the
-         * formatted data.
+         * Returns the constructed TLV formatted byte-array. This array is a copy of the wrapped
+         * or allocated array - truncated to just the significant bytes - i.e. those written into
+         * the (T)LV.
          *
          * @return The byte array containing the TLV formatted structure.
          */
         public byte[] getArray() {
-            return mArray;
+            return Arrays.copyOf(mArray, getActualLength());
         }
 
         /**
@@ -250,7 +256,7 @@
          *
          * @return The size of the TLV formatted portion of the byte array.
          */
-        public int getActualLength() {
+        private int getActualLength() {
             return mPosition;
         }
 
@@ -307,7 +313,7 @@
          */
         public int mOffset;
 
-        private TlvElement(int type, int length, byte[] refArray, int offset) {
+        private TlvElement(int type, int length, @Nullable byte[] refArray, int offset) {
             mType = type;
             mLength = length;
             mRefArray = refArray;
@@ -389,10 +395,8 @@
          * @param lengthSize Number of bytes sued for the Length (L) field.
          *            Values values are 1 or 2 bytes.
          * @param array The TLV formatted byte-array to parse.
-         * @param length The number of bytes of the array to be used in the
-         *            parsing.
          */
-        public TlvIterable(int typeSize, int lengthSize, byte[] array, int length) {
+        public TlvIterable(int typeSize, int lengthSize, @Nullable byte[] array) {
             if (typeSize < 0 || typeSize > 2 || lengthSize <= 0 || lengthSize > 2) {
                 throw new IllegalArgumentException(
                         "Invalid sizes - typeSize=" + typeSize + ", lengthSize=" + lengthSize);
@@ -400,7 +404,7 @@
             mTypeSize = typeSize;
             mLengthSize = lengthSize;
             mArray = array;
-            mArrayLength = length;
+            mArrayLength = (array == null) ? 0 : array.length;
         }
 
         /**
@@ -494,12 +498,11 @@
      * fields correctly fill the specified length (and do not overshoot).
      *
      * @param array The (T)LV array to verify.
-     * @param length The number of bytes in the array to consider (starting at offset 0).
      * @param typeSize The size (in bytes) of the type field. Valid values are 0, 1, or 2.
      * @param lengthSize The size (in bytes) of the length field. Valid values are 1 or 2.
      * @return A boolean indicating whether the array is valid (true) or invalid (false).
      */
-    public static boolean isValid(byte[] array, int length, int typeSize, int lengthSize) {
+    public static boolean isValid(@Nullable byte[] array, int typeSize, int lengthSize) {
         if (typeSize < 0 || typeSize > 2) {
             throw new IllegalArgumentException(
                     "Invalid arguments - typeSize must be 0, 1, or 2: typeSize=" + typeSize);
@@ -508,14 +511,12 @@
             throw new IllegalArgumentException(
                     "Invalid arguments - lengthSize must be 1 or 2: lengthSize=" + lengthSize);
         }
-        if (length < 0 || length > array.length) {
-            throw new IllegalArgumentException(
-                    "Invalid arguments - length must be non-negative and <= array.length: length="
-                            + length + ", array.length=" + array.length);
+        if (array == null) {
+            return true;
         }
 
         int nextTlvIndex = 0;
-        while (nextTlvIndex + typeSize + lengthSize <= length) {
+        while (nextTlvIndex + typeSize + lengthSize <= array.length) {
             nextTlvIndex += typeSize;
             if (lengthSize == 1) {
                 nextTlvIndex += lengthSize + array[nextTlvIndex];
@@ -525,6 +526,6 @@
             }
         }
 
-        return nextTlvIndex == length;
+        return nextTlvIndex == array.length;
     }
 }
diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java
index a58cdb7..ff612fe7 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanManager.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java
@@ -22,6 +22,8 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
+import android.net.ConnectivityManager;
+import android.net.NetworkRequest;
 import android.net.wifi.RttManager;
 import android.os.Binder;
 import android.os.Bundle;
@@ -205,15 +207,15 @@
 
     /**
      * Data-path creation role is that of INITIATOR. Used in
-     * {@link #createNetworkSpecifier(int, byte[], byte[], int)} and
-     * {@link WifiNanSession#createNetworkSpecifier(int, int, byte[], int)}.
+     * {@link #createNetworkSpecifier(int, byte[], byte[])} and
+     * {@link WifiNanSession#createNetworkSpecifier(int, int, byte[])}.
      */
     public static final int WIFI_NAN_DATA_PATH_ROLE_INITIATOR = 0;
 
     /**
      * Data-path creation role is that of RESPONDER. Used in
-     * {@link #createNetworkSpecifier(int, byte[], byte[], int)} and
-     * {@link WifiNanSession#createNetworkSpecifier(int, int, byte[], int)}.
+     * {@link #createNetworkSpecifier(int, byte[], byte[])} and
+     * {@link WifiNanSession#createNetworkSpecifier(int, int, byte[])}.
      */
 
     public static final int WIFI_NAN_DATA_PATH_ROLE_RESPONDER = 1;
@@ -524,12 +526,11 @@
     /**
      * {@hide}
      */
-    public void sendMessage(int sessionId, int peerId, byte[] message, int messageLength,
-            int messageId, int retryCount) {
+    public void sendMessage(int sessionId, int peerId, byte[] message, int messageId,
+            int retryCount) {
         if (VDBG) {
             Log.v(TAG, "sendMessage(): sessionId=" + sessionId + ", peerId=" + peerId
-                    + ", messageLength=" + messageLength + ", messageId=" + messageId
-                    + ", retryCount=" + retryCount);
+                    + ", messageId=" + messageId + ", retryCount=" + retryCount);
         }
 
         int clientId;
@@ -543,8 +544,7 @@
         }
 
         try {
-            mService.sendMessage(clientId, sessionId, peerId, message, messageLength, messageId,
-                    retryCount);
+            mService.sendMessage(clientId, sessionId, peerId, message, messageId, retryCount);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
@@ -587,10 +587,10 @@
      * {@hide}
      */
     public String createNetworkSpecifier(@DataPathRole int role, int sessionId, int peerId,
-            byte[] token, int tokenLength) {
+            byte[] token) {
         if (VDBG) {
             Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId
-                    + ", peerId=" + peerId + ", token=" + token + ", tokenLength=" + tokenLength);
+                    + ", peerId=" + peerId + ", token=" + token);
         }
 
         int type;
@@ -621,10 +621,6 @@
                                 + "INITIATOR");
             }
         }
-        if (tokenLength != 0 && (token == null || token.length < tokenLength)) {
-            throw new IllegalArgumentException(
-                    "Non-matching combination of token and tokenLength");
-        }
 
         int clientId;
         synchronized (mLock) {
@@ -650,7 +646,7 @@
             }
             if (token != null) {
                 json.put(NETWORK_SPECIFIER_KEY_TOKEN,
-                        Base64.encodeToString(token, 0, tokenLength, Base64.DEFAULT));
+                        Base64.encodeToString(token, 0, token.length, Base64.DEFAULT));
             }
         } catch (JSONException e) {
             return "";
@@ -674,19 +670,15 @@
      *              data-path setup process. On the RESPONDER a null token is permitted and
      *              matches any peer token - an empty token requires the peer token to be empty
      *              as well.
-     * @param tokenLength The number of significant (usable) bytes from the {@code token} parameter.
      * @return A string to be used to construct
      * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to {@link
-     * android.net.ConnectivityManager#requestNetwork(NetworkRequest,
-     * ConnectivityManager.NetworkCallback)}
-     * [or other varierties of that API].
+     * android.net.ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}
+     * [or other varieties of that API].
      */
     public String createNetworkSpecifier(@DataPathRole int role, @Nullable byte[] peer,
-            @Nullable byte[] token, int tokenLength) {
+            @Nullable byte[] token) {
         if (VDBG) {
-            Log.v(TAG,
-                    "createNetworkSpecifier: role=" + role + ", token=" + token + ", tokenLength="
-                            + tokenLength);
+            Log.v(TAG, "createNetworkSpecifier: role=" + role + ", token=" + token);
         }
 
         int type;
@@ -721,10 +713,6 @@
                         "createNetworkSpecifier: Invalid peer MAC address");
             }
         }
-        if (tokenLength != 0 && (token == null || token.length < tokenLength)) {
-            throw new IllegalArgumentException(
-                    "Non-matching combination of token and tokenLength");
-        }
 
         int clientId;
         synchronized (mLock) {
@@ -749,7 +737,7 @@
             }
             if (token != null) {
                 json.put(NETWORK_SPECIFIER_KEY_TOKEN,
-                        Base64.encodeToString(token, 0, tokenLength, Base64.DEFAULT));
+                        Base64.encodeToString(token, 0, token.length, Base64.DEFAULT));
             }
         } catch (JSONException e) {
             return "";
@@ -932,7 +920,6 @@
         private static final int CALLBACK_MESSAGE_SEND_FAIL = 6;
         private static final int CALLBACK_MESSAGE_RECEIVED = 7;
 
-        private static final String MESSAGE_BUNDLE_KEY_PEER_ID = "peer_id";
         private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message";
         private static final String MESSAGE_BUNDLE_KEY_MESSAGE2 = "message2";
 
@@ -983,11 +970,9 @@
                             break;
                         case CALLBACK_MATCH:
                             mOriginalCallback.onMatch(
-                                    msg.getData().getInt(MESSAGE_BUNDLE_KEY_PEER_ID),
-                                    msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
                                     msg.arg1,
-                                    msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2),
-                                    msg.arg2);
+                                    msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
+                                    msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2));
                             break;
                         case CALLBACK_MESSAGE_SEND_SUCCESS:
                             mOriginalCallback.onMessageSendSuccess(msg.arg1);
@@ -996,9 +981,7 @@
                             mOriginalCallback.onMessageSendFail(msg.arg1, msg.arg2);
                             break;
                         case CALLBACK_MESSAGE_RECEIVED:
-                            mOriginalCallback.onMessageReceived(msg.arg2,
-                                    msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
-                                    msg.arg1);
+                            mOriginalCallback.onMessageReceived(msg.arg1, (byte[]) msg.obj);
                             break;
                     }
                 }
@@ -1041,18 +1024,15 @@
         }
 
         @Override
-        public void onMatch(int peerId, byte[] serviceSpecificInfo,
-                int serviceSpecificInfoLength, byte[] matchFilter, int matchFilterLength) {
+        public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter) {
             if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId);
 
             Bundle data = new Bundle();
-            data.putInt(MESSAGE_BUNDLE_KEY_PEER_ID, peerId);
             data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, serviceSpecificInfo);
             data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2, matchFilter);
 
             Message msg = mHandler.obtainMessage(CALLBACK_MATCH);
-            msg.arg1 = serviceSpecificInfoLength;
-            msg.arg2 = matchFilterLength;
+            msg.arg1 = peerId;
             msg.setData(data);
             mHandler.sendMessage(msg);
         }
@@ -1077,19 +1057,14 @@
         }
 
         @Override
-        public void onMessageReceived(int peerId, byte[] message, int messageLength) {
+        public void onMessageReceived(int peerId, byte[] message) {
             if (VDBG) {
-                Log.v(TAG, "onMessageReceived: peerId='" + peerId + "', messageLength="
-                        + messageLength);
+                Log.v(TAG, "onMessageReceived: peerId='" + peerId);
             }
 
-            Bundle data = new Bundle();
-            data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, message);
-
             Message msg = mHandler.obtainMessage(CALLBACK_MESSAGE_RECEIVED);
-            msg.arg1 = messageLength;
-            msg.arg2 = peerId;
-            msg.setData(data);
+            msg.arg1 = peerId;
+            msg.obj = message;
             mHandler.sendMessage(msg);
         }
 
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/nan/WifiNanSession.java
index 8eb2f89..c10cd52 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSession.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSession.java
@@ -108,12 +108,11 @@
     /**
      * Sends a message to the specified destination. Message transmission is part of the current
      * discovery session - i.e. executed subsequent to a publish/subscribe
-     * {@link WifiNanSessionCallback#onMatch(int, byte[], int, byte[], int)} event.
+     * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} event.
      *
      * @param peerId The peer's ID for the message. Must be a result of an
-     *            {@link WifiNanSessionCallback#onMatch(int, byte[], int, byte[], int)} event.
+     *            {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} event.
      * @param message The message to be transmitted.
-     * @param messageLength The number of bytes from the {@code message} to be transmitted.
      * @param messageId An arbitrary integer used by the caller to identify the message. The same
      *            integer ID will be returned in the callbacks indicated message send success or
      *            failure.
@@ -122,8 +121,7 @@
      *            (note: no retransmissions are attempted in other failure cases). A value of 0
      *            indicates no retries. Max possible value is {@link #MAX_SEND_RETRY_COUNT}.
      */
-    public void sendMessage(int peerId, byte[] message, int messageLength, int messageId,
-            int retryCount) {
+    public void sendMessage(int peerId, @Nullable byte[] message, int messageId, int retryCount) {
         if (mTerminated) {
             Log.w(TAG, "sendMessage: called on terminated session");
             return;
@@ -134,33 +132,32 @@
                 return;
             }
 
-            mgr.sendMessage(mSessionId, peerId, message, messageLength, messageId, retryCount);
+            mgr.sendMessage(mSessionId, peerId, message, messageId, retryCount);
         }
     }
 
     /**
      * Sends a message to the specified destination. Message transmission is part of the current
      * discovery session - i.e. executed subsequent to a publish/subscribe
-     * {@link WifiNanSessionCallback#onMatch(int, byte[], int, byte[], int)} event. This is
-     * equivalent to {@link #sendMessage(int, byte[], int, int, int)} with a {@code retryCount} of
+     * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} event. This is
+     * equivalent to {@link #sendMessage(int, byte[], int, int)} with a {@code retryCount} of
      * 0.
      *
      * @param peerId The peer's ID for the message. Must be a result of an
-     *            {@link WifiNanSessionCallback#onMatch(int, byte[], int, byte[], int)} event.
+     *            {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} event.
      * @param message The message to be transmitted.
-     * @param messageLength The number of bytes from the {@code message} to be transmitted.
      * @param messageId An arbitrary integer used by the caller to identify the message. The same
      *            integer ID will be returned in the callbacks indicated message send success or
      *            failure.
      */
-    public void sendMessage(int peerId, byte[] message, int messageLength, int messageId) {
-        sendMessage(peerId, message, messageLength, messageId, 0);
+    public void sendMessage(int peerId, @Nullable byte[] message, int messageId) {
+        sendMessage(peerId, message, messageId, 0);
     }
 
     /**
      * Start a ranging operation with the specified peers. The peer IDs are obtained from an
-     * {@link WifiNanSessionCallback#onMatch(int, byte[], int, byte[], int)} or
-     * {@link WifiNanSessionCallback#onMessageReceived(int, byte[], int)} operation - i.e. can only
+     * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} or
+     * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])} operation - i.e. can only
      * range devices which are part of an ongoing discovery session.
      *
      * @param params   RTT parameters - each corresponding to a specific peer ID (the array sizes
@@ -194,22 +191,20 @@
      * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_INITIATOR} or
      * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_RESPONDER}
      * @param peerId The peer ID obtained through
-     * {@link WifiNanSessionCallback#onMatch(int, byte[], int, byte[], int)} or
-     * {@link WifiNanSessionCallback#onMessageReceived(int, byte[], int)}. On the RESPONDER a
+     * {@link WifiNanSessionCallback#onMatch(int, byte[], byte[])} or
+     * {@link WifiNanSessionCallback#onMessageReceived(int, byte[])}. On the RESPONDER a
      *               value of 0 is permitted which matches any peer.
      * @param token An arbitrary token (message) to be passed to the peer as part of the
      *              data-path setup process. On the RESPONDER a null token is permitted and
      *              matches any peer token - an empty token requires the peer token to be empty
      *              as well.
-     * @param tokenLength The number of significant (usable) bytes from the {@code token} parameter.
      * @return A string to be used to construct
      * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to {@link
      * android.net.ConnectivityManager#requestNetwork(NetworkRequest,
-     * ConnectivityManager.NetworkCallback)}
-     * [or other varierties of that API].
+     * ConnectivityManager.NetworkCallback)} [or other varieties of that API].
      */
     public String createNetworkSpecifier(@WifiNanManager.DataPathRole int role, int peerId,
-            @Nullable byte[] token, int tokenLength) {
+            @Nullable byte[] token) {
         if (mTerminated) {
             Log.w(TAG, "createNetworkSpecifier: called on terminated session");
             return null;
@@ -220,7 +215,7 @@
                 return null;
             }
 
-            return mgr.createNetworkSpecifier(role, mSessionId, peerId, token, tokenLength);
+            return mgr.createNetworkSpecifier(role, mSessionId, peerId, token);
         }
     }
 }
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSessionCallback.java b/wifi/java/android/net/wifi/nan/WifiNanSessionCallback.java
index b1f4100..f2337dd 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSessionCallback.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanSessionCallback.java
@@ -156,14 +156,10 @@
      * @param serviceSpecificInfo The service specific information (arbitrary
      *            byte array) provided by the peer as part of its discovery
      *            packet.
-     * @param serviceSpecificInfoLength The length of the service specific
-     *            information array.
      * @param matchFilter The filter (Tx on advertiser and Rx on listener) which
      *            resulted in this match.
-     * @param matchFilterLength The length of the match filter array.
      */
-    public void onMatch(int peerId, byte[] serviceSpecificInfo,
-            int serviceSpecificInfoLength, byte[] matchFilter, int matchFilterLength) {
+    public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter) {
         /* empty */
     }
 
@@ -204,10 +200,8 @@
      *
      * @param peerId The ID of the peer sending the message.
      * @param message A byte array containing the message.
-     * @param messageLength The length of the byte array containing the relevant
-     *            message bytes.
      */
-    public void onMessageReceived(int peerId, byte[] message, int messageLength) {
+    public void onMessageReceived(int peerId, byte[] message) {
         /* empty */
     }
 }