Merge "Importance slider logging." into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 649d7fa..40ead48 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -198,7 +198,6 @@
 
   public static final class R.attr {
     ctor public R.attr();
-    field public static final int abiOverride = 16844054; // 0x1010516
     field public static final int absListViewStyle = 16842858; // 0x101006a
     field public static final int accessibilityEventTypes = 16843648; // 0x1010380
     field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -1365,6 +1364,7 @@
     field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
     field public static final int unselectedAlpha = 16843278; // 0x101020e
     field public static final int updatePeriodMillis = 16843344; // 0x1010250
+    field public static final int use32bitAbi = 16844054; // 0x1010516
     field public static final int useDefaultMargins = 16843641; // 0x1010379
     field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
     field public static final int useLevel = 16843167; // 0x101019f
@@ -40661,6 +40661,21 @@
     method public static android.view.FocusFinder getInstance();
   }
 
+  public final class FrameMetrics {
+    ctor public FrameMetrics(android.view.FrameMetrics);
+    method public long getMetric(int);
+    field public static final int ANIMATION_DURATION = 2; // 0x2
+    field public static final int COMMAND_ISSUE_DURATION = 6; // 0x6
+    field public static final int DRAW_DURATION = 4; // 0x4
+    field public static final int FIRST_DRAW_FRAME = 9; // 0x9
+    field public static final int INPUT_HANDLING_DURATION = 1; // 0x1
+    field public static final int LAYOUT_MEASURE_DURATION = 3; // 0x3
+    field public static final int SWAP_BUFFERS_DURATION = 7; // 0x7
+    field public static final int SYNC_DURATION = 5; // 0x5
+    field public static final int TOTAL_DURATION = 8; // 0x8
+    field public static final int UNKNOWN_DELAY_DURATION = 0; // 0x0
+  }
+
   public abstract class FrameStats {
     ctor public FrameStats();
     method public final long getEndTimeNano();
@@ -43164,6 +43179,7 @@
     ctor public Window(android.content.Context);
     method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
     method public void addFlags(int);
+    method public final void addFrameMetricsListener(android.view.Window.FrameMetricsListener, android.os.Handler);
     method public void clearFlags(int);
     method public abstract void closeAllPanels();
     method public abstract void closePanel(int);
@@ -43215,6 +43231,7 @@
     method public abstract boolean performContextMenuIdentifierAction(int, int);
     method public abstract boolean performPanelIdentifierAction(int, int, int);
     method public abstract boolean performPanelShortcut(int, int, android.view.KeyEvent, int);
+    method public final void removeFrameMetricsListener(android.view.Window.FrameMetricsListener);
     method public boolean requestFeature(int);
     method public abstract void restoreHierarchyState(android.os.Bundle);
     method public abstract android.os.Bundle saveHierarchyState();
@@ -43340,6 +43357,10 @@
     method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
   }
 
+  public static abstract interface Window.FrameMetricsListener {
+    method public abstract void onMetricsAvailable(android.view.Window, android.view.FrameMetrics, int);
+  }
+
   public static abstract interface Window.OnRestrictedCaptionAreaChangedListener {
     method public abstract void onRestrictedCaptionAreaChanged(android.graphics.Rect);
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 3c3054e..dfe1e08 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -293,7 +293,6 @@
 
   public static final class R.attr {
     ctor public R.attr();
-    field public static final int abiOverride = 16844054; // 0x1010516
     field public static final int absListViewStyle = 16842858; // 0x101006a
     field public static final int accessibilityEventTypes = 16843648; // 0x1010380
     field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -1464,6 +1463,7 @@
     field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
     field public static final int unselectedAlpha = 16843278; // 0x101020e
     field public static final int updatePeriodMillis = 16843344; // 0x1010250
+    field public static final int use32bitAbi = 16844054; // 0x1010516
     field public static final int useDefaultMargins = 16843641; // 0x1010379
     field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
     field public static final int useLevel = 16843167; // 0x101019f
@@ -43413,6 +43413,21 @@
     method public static android.view.FocusFinder getInstance();
   }
 
+  public final class FrameMetrics {
+    ctor public FrameMetrics(android.view.FrameMetrics);
+    method public long getMetric(int);
+    field public static final int ANIMATION_DURATION = 2; // 0x2
+    field public static final int COMMAND_ISSUE_DURATION = 6; // 0x6
+    field public static final int DRAW_DURATION = 4; // 0x4
+    field public static final int FIRST_DRAW_FRAME = 9; // 0x9
+    field public static final int INPUT_HANDLING_DURATION = 1; // 0x1
+    field public static final int LAYOUT_MEASURE_DURATION = 3; // 0x3
+    field public static final int SWAP_BUFFERS_DURATION = 7; // 0x7
+    field public static final int SYNC_DURATION = 5; // 0x5
+    field public static final int TOTAL_DURATION = 8; // 0x8
+    field public static final int UNKNOWN_DELAY_DURATION = 0; // 0x0
+  }
+
   public abstract class FrameStats {
     ctor public FrameStats();
     method public final long getEndTimeNano();
@@ -45916,6 +45931,7 @@
     ctor public Window(android.content.Context);
     method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
     method public void addFlags(int);
+    method public final void addFrameMetricsListener(android.view.Window.FrameMetricsListener, android.os.Handler);
     method public void clearFlags(int);
     method public abstract void closeAllPanels();
     method public abstract void closePanel(int);
@@ -45967,6 +45983,7 @@
     method public abstract boolean performContextMenuIdentifierAction(int, int);
     method public abstract boolean performPanelIdentifierAction(int, int, int);
     method public abstract boolean performPanelShortcut(int, int, android.view.KeyEvent, int);
+    method public final void removeFrameMetricsListener(android.view.Window.FrameMetricsListener);
     method public boolean requestFeature(int);
     method public abstract void restoreHierarchyState(android.os.Bundle);
     method public abstract android.os.Bundle saveHierarchyState();
@@ -46093,6 +46110,10 @@
     method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
   }
 
+  public static abstract interface Window.FrameMetricsListener {
+    method public abstract void onMetricsAvailable(android.view.Window, android.view.FrameMetrics, int);
+  }
+
   public static abstract interface Window.OnRestrictedCaptionAreaChangedListener {
     method public abstract void onRestrictedCaptionAreaChanged(android.graphics.Rect);
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index 8fbcff9..10733f9 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -198,7 +198,6 @@
 
   public static final class R.attr {
     ctor public R.attr();
-    field public static final int abiOverride = 16844054; // 0x1010516
     field public static final int absListViewStyle = 16842858; // 0x101006a
     field public static final int accessibilityEventTypes = 16843648; // 0x1010380
     field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -1365,6 +1364,7 @@
     field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
     field public static final int unselectedAlpha = 16843278; // 0x101020e
     field public static final int updatePeriodMillis = 16843344; // 0x1010250
+    field public static final int use32bitAbi = 16844054; // 0x1010516
     field public static final int useDefaultMargins = 16843641; // 0x1010379
     field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
     field public static final int useLevel = 16843167; // 0x101019f
@@ -40678,6 +40678,21 @@
     method public static android.view.FocusFinder getInstance();
   }
 
+  public final class FrameMetrics {
+    ctor public FrameMetrics(android.view.FrameMetrics);
+    method public long getMetric(int);
+    field public static final int ANIMATION_DURATION = 2; // 0x2
+    field public static final int COMMAND_ISSUE_DURATION = 6; // 0x6
+    field public static final int DRAW_DURATION = 4; // 0x4
+    field public static final int FIRST_DRAW_FRAME = 9; // 0x9
+    field public static final int INPUT_HANDLING_DURATION = 1; // 0x1
+    field public static final int LAYOUT_MEASURE_DURATION = 3; // 0x3
+    field public static final int SWAP_BUFFERS_DURATION = 7; // 0x7
+    field public static final int SYNC_DURATION = 5; // 0x5
+    field public static final int TOTAL_DURATION = 8; // 0x8
+    field public static final int UNKNOWN_DELAY_DURATION = 0; // 0x0
+  }
+
   public abstract class FrameStats {
     ctor public FrameStats();
     method public final long getEndTimeNano();
@@ -43181,6 +43196,7 @@
     ctor public Window(android.content.Context);
     method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
     method public void addFlags(int);
+    method public final void addFrameMetricsListener(android.view.Window.FrameMetricsListener, android.os.Handler);
     method public void clearFlags(int);
     method public abstract void closeAllPanels();
     method public abstract void closePanel(int);
@@ -43232,6 +43248,7 @@
     method public abstract boolean performContextMenuIdentifierAction(int, int);
     method public abstract boolean performPanelIdentifierAction(int, int, int);
     method public abstract boolean performPanelShortcut(int, int, android.view.KeyEvent, int);
+    method public final void removeFrameMetricsListener(android.view.Window.FrameMetricsListener);
     method public boolean requestFeature(int);
     method public abstract void restoreHierarchyState(android.os.Bundle);
     method public abstract android.os.Bundle saveHierarchyState();
@@ -43357,6 +43374,10 @@
     method public abstract android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
   }
 
+  public static abstract interface Window.FrameMetricsListener {
+    method public abstract void onMetricsAvailable(android.view.Window, android.view.FrameMetrics, int);
+  }
+
   public static abstract interface Window.OnRestrictedCaptionAreaChangedListener {
     method public abstract void onRestrictedCaptionAreaChanged(android.graphics.Rect);
   }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 72ace15..5dddebd 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -337,7 +337,7 @@
 
         public final boolean coreApp;
         public final boolean multiArch;
-        public final String abiOverride;
+        public final boolean use32bitAbi;
         public final boolean extractNativeLibs;
 
         public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
@@ -354,7 +354,7 @@
             this.splitRevisionCodes = splitRevisionCodes;
             this.coreApp = baseApk.coreApp;
             this.multiArch = baseApk.multiArch;
-            this.abiOverride = baseApk.abiOverride;
+            this.use32bitAbi = baseApk.use32bitAbi;
             this.extractNativeLibs = baseApk.extractNativeLibs;
         }
 
@@ -382,12 +382,12 @@
         public final Signature[] signatures;
         public final boolean coreApp;
         public final boolean multiArch;
-        public final String abiOverride;
+        public final boolean use32bitAbi;
         public final boolean extractNativeLibs;
 
         public ApkLite(String codePath, String packageName, String splitName, int versionCode,
                 int revisionCode, int installLocation, List<VerifierInfo> verifiers,
-                Signature[] signatures, boolean coreApp, boolean multiArch, String abiOverride,
+                Signature[] signatures, boolean coreApp, boolean multiArch, boolean use32bitAbi,
                 boolean extractNativeLibs) {
             this.codePath = codePath;
             this.packageName = packageName;
@@ -399,7 +399,7 @@
             this.signatures = signatures;
             this.coreApp = coreApp;
             this.multiArch = multiArch;
-            this.abiOverride = abiOverride;
+            this.use32bitAbi = use32bitAbi;
             this.extractNativeLibs = extractNativeLibs;
         }
     }
@@ -843,8 +843,7 @@
             }
 
             pkg.setCodePath(packageDir.getAbsolutePath());
-            pkg.setCpuAbiOverride(lite.abiOverride);
-
+            pkg.setUse32bitAbi(lite.use32bitAbi);
             return pkg;
         } finally {
             IoUtils.closeQuietly(assets);
@@ -875,7 +874,7 @@
         try {
             final Package pkg = parseBaseApk(apkFile, assets, flags);
             pkg.setCodePath(apkFile.getAbsolutePath());
-            pkg.setCpuAbiOverride(lite.abiOverride);
+            pkg.setUse32bitAbi(lite.use32bitAbi);
             return pkg;
         } finally {
             IoUtils.closeQuietly(assets);
@@ -1380,7 +1379,7 @@
         int revisionCode = 0;
         boolean coreApp = false;
         boolean multiArch = false;
-        String abiOverride = null;
+        boolean use32bitAbi = false;
         boolean extractNativeLibs = true;
 
         for (int i = 0; i < attrs.getAttributeCount(); i++) {
@@ -1421,8 +1420,8 @@
                     if ("multiArch".equals(attr)) {
                         multiArch = attrs.getAttributeBooleanValue(i, false);
                     }
-                    if ("abiOverride".equals(attr)) {
-                        abiOverride = attrs.getAttributeValue(i);
+                    if ("use32bitAbi".equals(attr)) {
+                        use32bitAbi = attrs.getAttributeBooleanValue(i, false);
                     }
                     if ("extractNativeLibs".equals(attr)) {
                         extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
@@ -1433,7 +1432,7 @@
 
         return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
                 revisionCode, installLocation, verifiers, signatures, coreApp, multiArch,
-                abiOverride, extractNativeLibs);
+                use32bitAbi, extractNativeLibs);
     }
 
     /**
@@ -4740,6 +4739,12 @@
          * and prods fields out of {@code this.applicationInfo}.
          */
         public String cpuAbiOverride;
+        /**
+         * The install time abi override to choose 32bit abi's when multiple abi's
+         * are present. This is only meaningfull for multiarch applications.
+         * The use32bitAbi attribute is ignored if cpuAbiOverride is also set.
+         */
+        public boolean use32bitAbi;
 
         public Package(String packageName) {
             this.packageName = packageName;
@@ -4872,12 +4877,12 @@
             }
         }
 
-        public void setCpuAbiOverride(String cpuAbiOverride) {
-            this.cpuAbiOverride = cpuAbiOverride;
+        public void setUse32bitAbi(boolean use32bitAbi) {
+            this.use32bitAbi = use32bitAbi;
             if (childPackages != null) {
                 final int packageCount = childPackages.size();
                 for (int i = 0; i < packageCount; i++) {
-                    childPackages.get(i).cpuAbiOverride = cpuAbiOverride;
+                    childPackages.get(i).use32bitAbi = use32bitAbi;
                 }
             }
         }
diff --git a/core/java/android/view/FrameMetrics.java b/core/java/android/view/FrameMetrics.java
new file mode 100644
index 0000000..8e66f86
--- /dev/null
+++ b/core/java/android/view/FrameMetrics.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.IntDef;
+import android.view.Window;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Class containing timing data for various milestones in a frame
+ * lifecycle reported by the rendering subsystem.
+ * <p>
+ * Supported metrics can be queried via their corresponding identifier.
+ * </p>
+ */
+public final class FrameMetrics {
+
+    /**
+     * Metric identifier for unknown delay.
+     * <p>
+     * Represents the number of nanoseconds elapsed waiting for the
+     * UI thread to become responsive and process the frame. This
+     * should be 0 most of the time.
+     * </p>
+     */
+    public static final int UNKNOWN_DELAY_DURATION = 0;
+
+    /**
+     * Metric identifier for input handling duration.
+     * <p>
+     * Represents the number of nanoseconds elapsed issuing
+     * input handling callbacks.
+     * </p>
+     */
+    public static final int INPUT_HANDLING_DURATION = 1;
+
+    /**
+     * Metric identifier for animation callback duration.
+     * <p>
+     * Represents the number of nanoseconds elapsed issuing
+     * animation callbacks.
+     * </p>
+     */
+    public static final int ANIMATION_DURATION = 2;
+
+    /**
+     * Metric identifier for layout/measure duration.
+     * <p>
+     * Represents the number of nanoseconds elapsed measuring
+     * and laying out the invalidated pieces of the view hierarchy.
+     * </p>
+     */
+    public static final int LAYOUT_MEASURE_DURATION = 3;
+    /**
+     * Metric identifier for draw duration.
+     * <p>
+     * Represents the number of nanoseconds elapsed computing
+     * DisplayLists for transformations applied to the view
+     * hierarchy.
+     * </p>
+     */
+    public static final int DRAW_DURATION = 4;
+
+    /**
+     * Metric identifier for sync duration.
+     * <p>
+     * Represents the number of nanoseconds elapsed
+     * synchronizing the computed display lists with the render
+     * thread.
+     * </p>
+     */
+    public static final int SYNC_DURATION = 5;
+
+    /**
+     * Metric identifier for command issue duration.
+     * <p>
+     * Represents the number of nanoseconds elapsed
+     * issuing draw commands to the GPU.
+     * </p>
+     */
+    public static final int COMMAND_ISSUE_DURATION = 6;
+
+    /**
+     * Metric identifier for swap buffers duration.
+     * <p>
+     * Represents the number of nanoseconds elapsed issuing
+     * the frame buffer for this frame to the display
+     * subsystem.
+     * </p>
+     */
+    public static final int SWAP_BUFFERS_DURATION = 7;
+
+    /**
+     * Metric identifier for total frame duration.
+     * <p>
+     * Represents the total time in nanoseconds this frame took to render
+     * and be issued to the display subsystem.
+     * </p>
+     * <p>
+     * Equal to the sum of the values of all other time-valued metric
+     * identifiers.
+     * </p>
+     */
+    public static final int TOTAL_DURATION = 8;
+
+    /**
+     * Metric identifier for a boolean value determining whether this frame was
+     * the first to draw in a new Window layout.
+     * <p>
+     * {@link #getMetric(int)} will return 0 for false, 1 for true.
+     * </p>
+     * <p>
+     * First draw frames are expected to be slow and should usually be exempt
+     * from display jank calculations as they do not cause skips in animations
+     * and are usually hidden by window animations or other tricks.
+     * </p>
+     */
+    public static final int FIRST_DRAW_FRAME = 9;
+
+    private static final int FRAME_INFO_FLAG_FIRST_DRAW = 1 << 0;
+
+    /**
+     * Identifiers for metrics available for each frame.
+     *
+     * {@see {@link #getMetric(int)}}
+     * @hide
+     */
+    @IntDef({
+            UNKNOWN_DELAY_DURATION,
+            INPUT_HANDLING_DURATION,
+            ANIMATION_DURATION,
+            LAYOUT_MEASURE_DURATION,
+            DRAW_DURATION,
+            SYNC_DURATION,
+            COMMAND_ISSUE_DURATION,
+            SWAP_BUFFERS_DURATION,
+            TOTAL_DURATION,
+            FIRST_DRAW_FRAME,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Metric {}
+
+    /**
+     * Timestamp indices for frame milestones.
+     *
+     * May change from release to release.
+     *
+     * Must be kept in sync with frameworks/base/libs/hwui/FrameInfo.h.
+     *
+     * @hide
+     */
+    @IntDef ({
+            Index.FLAGS,
+            Index.INTENDED_VSYNC,
+            Index.VSYNC,
+            Index.OLDEST_INPUT_EVENT,
+            Index.NEWEST_INPUT_EVENT,
+            Index.HANDLE_INPUT_START,
+            Index.ANIMATION_START,
+            Index.PERFORM_TRAVERSALS_START,
+            Index.DRAW_START,
+            Index.SYNC_QUEUED,
+            Index.SYNC_START,
+            Index.ISSUE_DRAW_COMMANDS_START,
+            Index.SWAP_BUFFERS,
+            Index.FRAME_COMPLETED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface Index {
+        int FLAGS = 0;
+        int INTENDED_VSYNC = 1;
+        int VSYNC = 2;
+        int OLDEST_INPUT_EVENT = 3;
+        int NEWEST_INPUT_EVENT = 4;
+        int HANDLE_INPUT_START = 5;
+        int ANIMATION_START = 6;
+        int PERFORM_TRAVERSALS_START = 7;
+        int DRAW_START = 8;
+        int SYNC_QUEUED = 9;
+        int SYNC_START = 10;
+        int ISSUE_DRAW_COMMANDS_START = 11;
+        int SWAP_BUFFERS = 12;
+        int FRAME_COMPLETED = 13;
+
+        int FRAME_STATS_COUNT = 14; // must always be last
+    }
+
+    /*
+     * Bucket endpoints for each Metric defined above.
+     *
+     * Each defined metric *must* have a corresponding entry
+     * in this list.
+     */
+    private static final int[] DURATIONS = new int[] {
+        // UNKNOWN_DELAY
+        Index.INTENDED_VSYNC, Index.HANDLE_INPUT_START,
+        // INPUT_HANDLING
+        Index.HANDLE_INPUT_START, Index.ANIMATION_START,
+        // ANIMATION
+        Index.ANIMATION_START, Index.PERFORM_TRAVERSALS_START,
+        // LAYOUT_MEASURE
+        Index.PERFORM_TRAVERSALS_START, Index.DRAW_START,
+        // DRAW
+        Index.DRAW_START, Index.SYNC_QUEUED,
+        // SYNC
+        Index.SYNC_START, Index.ISSUE_DRAW_COMMANDS_START,
+        // COMMAND_ISSUE
+        Index.ISSUE_DRAW_COMMANDS_START, Index.SWAP_BUFFERS,
+        // SWAP_BUFFERS
+        Index.SWAP_BUFFERS, Index.FRAME_COMPLETED,
+        // TOTAL_DURATION
+        Index.INTENDED_VSYNC, Index.FRAME_COMPLETED,
+    };
+
+    /* package */ final long[] mTimingData;
+
+    /**
+     * Constructs a FrameMetrics object as a copy.
+     * <p>
+     * Use this method to copy out metrics reported by
+     * {@link Window.FrameMetricsListener#onMetricsAvailable(Window, FrameMetrics, int)}
+     * </p>
+     * @param other the FrameMetrics object to copy.
+     */
+    public FrameMetrics(FrameMetrics other) {
+        mTimingData = new long[Index.FRAME_STATS_COUNT];
+        System.arraycopy(other.mTimingData, 0, mTimingData, 0, mTimingData.length);
+    }
+
+    /**
+     * @hide
+     */
+    FrameMetrics() {
+        mTimingData = new long[Index.FRAME_STATS_COUNT];
+    }
+
+    /**
+     * Retrieves the value associated with Metric identifier {@code id}
+     * for this frame.
+     * <p>
+     * Boolean metrics are represented in [0,1], with 0 corresponding to
+     * false, and 1 corresponding to true.
+     * </p>
+     * @param id the metric to retrieve
+     * @return the value of the metric or -1 if it is not available.
+     */
+    public long getMetric(@Metric int id) {
+        if (id < UNKNOWN_DELAY_DURATION || id > FIRST_DRAW_FRAME) {
+            return -1;
+        }
+
+        if (mTimingData == null) {
+            return -1;
+        }
+
+        if (id == FIRST_DRAW_FRAME) {
+            return (mTimingData[Index.FLAGS] & FRAME_INFO_FLAG_FIRST_DRAW) != 0 ? 1 : 0;
+        }
+
+        int durationsIdx = 2 * id;
+        return mTimingData[DURATIONS[durationsIdx + 1]]
+                - mTimingData[DURATIONS[durationsIdx]];
+    }
+}
+
diff --git a/core/java/android/view/FrameMetricsObserver.java b/core/java/android/view/FrameMetricsObserver.java
new file mode 100644
index 0000000..f38f8b7
--- /dev/null
+++ b/core/java/android/view/FrameMetricsObserver.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.annotation.NonNull;
+import android.util.Log;
+import android.os.Looper;
+import android.os.MessageQueue;
+
+import com.android.internal.util.VirtualRefBasePtr;
+
+import java.lang.NullPointerException;
+import java.lang.ref.WeakReference;
+import java.lang.SuppressWarnings;
+
+/**
+ * Provides streaming access to frame stats information from the rendering
+ * subsystem to apps.
+ *
+ * @hide
+ */
+public class FrameMetricsObserver {
+    private MessageQueue mMessageQueue;
+
+    private WeakReference<Window> mWindow;
+
+    private FrameMetrics mFrameMetrics;
+
+    /* package */ Window.FrameMetricsListener mListener;
+    /* package */ VirtualRefBasePtr mNative;
+
+    /**
+     * Creates a FrameMetricsObserver
+     *
+     * @param looper the looper to use when invoking callbacks
+     */
+    FrameMetricsObserver(@NonNull Window window, @NonNull Looper looper,
+            @NonNull Window.FrameMetricsListener listener) {
+        if (looper == null) {
+            throw new NullPointerException("looper cannot be null");
+        }
+
+        mMessageQueue = looper.getQueue();
+        if (mMessageQueue == null) {
+            throw new IllegalStateException("invalid looper, null message queue\n");
+        }
+
+        mFrameMetrics = new FrameMetrics();
+        mWindow = new WeakReference<>(window);
+        mListener = listener;
+    }
+
+    // Called by native on the provided Handler
+    @SuppressWarnings("unused")
+    private void notifyDataAvailable(int dropCount) {
+        final Window window = mWindow.get();
+        if (window != null) {
+            mListener.onMetricsAvailable(window, mFrameMetrics, dropCount);
+        }
+    }
+}
diff --git a/core/java/android/view/FrameStatsObserver.java b/core/java/android/view/FrameStatsObserver.java
deleted file mode 100644
index 0add607..0000000
--- a/core/java/android/view/FrameStatsObserver.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.annotation.NonNull;
-import android.util.Log;
-import android.os.Looper;
-import android.os.MessageQueue;
-
-import com.android.internal.util.VirtualRefBasePtr;
-
-import java.lang.NullPointerException;
-import java.lang.ref.WeakReference;
-import java.lang.SuppressWarnings;
-
-/**
- * Provides streaming access to frame stats information from the rendering
- * subsystem to apps.
- *
- * @hide
- */
-public abstract class FrameStatsObserver {
-    private static final String TAG = "FrameStatsObserver";
-
-    private MessageQueue mMessageQueue;
-    private long[] mBuffer;
-
-    private FrameStats mFrameStats;
-
-    /* package */ ThreadedRenderer mRenderer;
-    /* package */ VirtualRefBasePtr mNative;
-
-    /**
-     * Containing class for frame statistics reported
-     * by the rendering subsystem.
-     */
-    public static class FrameStats {
-        /**
-         * Precise timing data for various milestones in a frame
-         * lifecycle.
-         *
-         * This data is exactly the same as what is returned by
-         * `adb shell dumpsys gfxinfo <PACKAGE_NAME> framestats`
-         *
-         * The fields reported may change from release to release.
-         *
-         * @see {@link http://developer.android.com/training/testing/performance.html}
-         * for a description of the fields present.
-         */
-        public long[] mTimingData;
-    }
-
-    /**
-     * Creates a FrameStatsObserver
-     *
-     * @param looper the looper to use when invoking callbacks
-     */
-    public FrameStatsObserver(@NonNull Looper looper) {
-        if (looper == null) {
-            throw new NullPointerException("looper cannot be null");
-        }
-
-        mMessageQueue = looper.getQueue();
-        if (mMessageQueue == null) {
-            throw new IllegalStateException("invalid looper, null message queue\n");
-        }
-
-        mFrameStats = new FrameStats();
-    }
-
-    /**
-     * Called on provided looper when frame stats data is available
-     * for the previous frame.
-     *
-     * Clients of this class must do as little work as possible within
-     * this callback, as the buffer is shared between the producer and consumer.
-     *
-     * If the consumer is still executing within this method when there is new
-     * data available that data will be dropped. The producer cannot
-     * wait on the consumer.
-     *
-     * @param data the newly available data
-     */
-    public abstract void onDataAvailable(FrameStats data);
-
-    /**
-     * Returns the number of reports dropped as a result of a slow
-     * consumer.
-     */
-    public long getDroppedReportCount() {
-        if (mRenderer == null) {
-            return 0;
-        }
-
-        return mRenderer.getDroppedFrameReportCount();
-    }
-
-    public boolean isRegistered() {
-        return mRenderer != null && mNative != null;
-    }
-
-    // === called by native === //
-    @SuppressWarnings("unused")
-    private void notifyDataAvailable() {
-        mFrameStats.mTimingData = mBuffer;
-        onDataAvailable(mFrameStats);
-    }
-}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 8b06ecf..ca41d78 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -354,8 +354,6 @@
     private boolean mEnabled;
     private boolean mRequested = true;
 
-    private HashSet<FrameStatsObserver> mFrameStatsObservers;
-
     ThreadedRenderer(Context context, boolean translucent) {
         final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0, 0);
         mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
@@ -964,29 +962,14 @@
         }
     }
 
-    void addFrameStatsObserver(FrameStatsObserver fso) {
-        if (mFrameStatsObservers == null) {
-            mFrameStatsObservers = new HashSet<>();
-        }
-
-        long nativeFso = nAddFrameStatsObserver(mNativeProxy, fso);
-        fso.mRenderer = this;
-        fso.mNative = new VirtualRefBasePtr(nativeFso);
-        mFrameStatsObservers.add(fso);
+    void addFrameMetricsObserver(FrameMetricsObserver observer) {
+        long nativeObserver = nAddFrameMetricsObserver(mNativeProxy, observer);
+        observer.mNative = new VirtualRefBasePtr(nativeObserver);
     }
 
-    void removeFrameStatsObserver(FrameStatsObserver fso) {
-        if (!mFrameStatsObservers.remove(fso)) {
-            throw new IllegalArgumentException("attempt to remove FrameStatsObserver that was never added");
-        }
-
-        nRemoveFrameStatsObserver(mNativeProxy, fso.mNative.get());
-        fso.mRenderer = null;
-        fso.mNative = null;
-    }
-
-    long getDroppedFrameReportCount() {
-        return nGetDroppedFrameReportCount(mNativeProxy);
+    void removeFrameMetricsObserver(FrameMetricsObserver observer) {
+        nRemoveFrameMetricsObserver(mNativeProxy, observer.mNative.get());
+        observer.mNative = null;
     }
 
     static native void setupShadersDiskCache(String cacheFile);
@@ -1044,7 +1027,6 @@
     private static native void nSetContentDrawBounds(long nativeProxy, int left,
              int top, int right, int bottom);
 
-    private static native long nAddFrameStatsObserver(long nativeProxy, FrameStatsObserver fso);
-    private static native void nRemoveFrameStatsObserver(long nativeProxy, long nativeFso);
-    private static native long nGetDroppedFrameReportCount(long nativeProxy);
+    private static native long nAddFrameMetricsObserver(long nativeProxy, FrameMetricsObserver observer);
+    private static native void nRemoveFrameMetricsObserver(long nativeProxy, long nativeObserver);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f52b2907..2612ab2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3703,9 +3703,9 @@
     private ViewPropertyAnimator mAnimator = null;
 
     /**
-     * List of FrameStatsObservers pending registration when mAttachInfo is null.
+     * List of registered FrameMetricsObservers.
      */
-    private ArrayList<FrameStatsObserver> mPendingFrameStatsObservers;
+    private ArrayList<FrameMetricsObserver> mFrameMetricsObservers;
 
     /**
      * Flag indicating that a drag can cross window boundaries.  When
@@ -5479,19 +5479,29 @@
      *
      * @hide
      */
-    public void addFrameStatsObserver(FrameStatsObserver fso) {
+    public void addFrameMetricsListener(Window window, Window.FrameMetricsListener listener,
+            Handler handler) {
         if (mAttachInfo != null) {
             if (mAttachInfo.mHardwareRenderer != null) {
-                mAttachInfo.mHardwareRenderer.addFrameStatsObserver(fso);
+                if (mFrameMetricsObservers == null) {
+                    mFrameMetricsObservers = new ArrayList<>();
+                }
+
+                FrameMetricsObserver fmo = new FrameMetricsObserver(window,
+                        handler.getLooper(), listener);
+                mFrameMetricsObservers.add(fmo);
+                mAttachInfo.mHardwareRenderer.addFrameMetricsObserver(fmo);
             } else {
                 Log.w(VIEW_LOG_TAG, "View not hardware-accelerated. Unable to observe frame stats");
             }
         } else {
-            if (mPendingFrameStatsObservers == null) {
-                mPendingFrameStatsObservers = new ArrayList<>();
+            if (mFrameMetricsObservers == null) {
+                mFrameMetricsObservers = new ArrayList<>();
             }
 
-            mPendingFrameStatsObservers.add(fso);
+            FrameMetricsObserver fmo = new FrameMetricsObserver(window,
+                    handler.getLooper(), listener);
+            mFrameMetricsObservers.add(fmo);
         }
     }
 
@@ -5500,32 +5510,45 @@
      *
      * @hide
      */
-    public void removeFrameStatsObserver(FrameStatsObserver fso) {
+    public void removeFrameMetricsListener(Window.FrameMetricsListener listener) {
         ThreadedRenderer renderer = getHardwareRenderer();
-
-        if (mPendingFrameStatsObservers != null) {
-            mPendingFrameStatsObservers.remove(fso);
+        FrameMetricsObserver fmo = findFrameMetricsObserver(listener);
+        if (fmo == null) {
+            throw new IllegalArgumentException("attempt to remove FrameMetricsListener that was never added");
         }
 
-        if (renderer != null) {
-            renderer.removeFrameStatsObserver(fso);
+        if (mFrameMetricsObservers != null) {
+            mFrameMetricsObservers.remove(fmo);
+            if (renderer != null) {
+                renderer.removeFrameMetricsObserver(fmo);
+            }
         }
     }
 
-    private void registerPendingFrameStatsObservers() {
-        if (mPendingFrameStatsObservers != null) {
+    private void registerPendingFrameMetricsObservers() {
+        if (mFrameMetricsObservers != null) {
             ThreadedRenderer renderer = getHardwareRenderer();
             if (renderer != null) {
-                for (FrameStatsObserver fso : mPendingFrameStatsObservers) {
-                    renderer.addFrameStatsObserver(fso);
+                for (FrameMetricsObserver fmo : mFrameMetricsObservers) {
+                    renderer.addFrameMetricsObserver(fmo);
                 }
             } else {
                 Log.w(VIEW_LOG_TAG, "View not hardware-accelerated. Unable to observe frame stats");
             }
-            mPendingFrameStatsObservers = null;
         }
     }
 
+    private FrameMetricsObserver findFrameMetricsObserver(Window.FrameMetricsListener listener) {
+        for (int i = 0; i < mFrameMetricsObservers.size(); i++) {
+            FrameMetricsObserver observer = mFrameMetricsObservers.get(i);
+            if (observer.mListener == listener) {
+                return observer;
+            }
+        }
+
+        return null;
+    }
+
     /**
      * Call this view's OnClickListener, if it is defined.  Performs all normal
      * actions associated with clicking: reporting accessibility event, playing
@@ -15160,7 +15183,7 @@
             mFloatingTreeObserver = null;
         }
 
-        registerPendingFrameStatsObservers();
+        registerPendingFrameMetricsObservers();
 
         if ((mPrivateFlags&PFLAG_SCROLL_CONTAINER) != 0) {
             mAttachInfo.mScrollContainers.add(this);
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index c68a740..9f05990 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -34,6 +34,7 @@
 import android.media.session.MediaController;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -604,6 +605,34 @@
         void onRestrictedCaptionAreaChanged(Rect rect);
     }
 
+    /**
+     * Callback for clients that want frame timing information for each
+     * frame rendered by the Window.
+     */
+    public interface FrameMetricsListener {
+        /**
+         * Called when information is available for the previously rendered frame.
+         *
+         * Reports can be dropped if this callback takes too
+         * long to execute, as the report producer cannot wait for the consumer to
+         * complete.
+         *
+         * It is highly recommended that clients copy the passed in FrameMetrics
+         * via {@link FrameMetrics#FrameMetrics(FrameMetrics)} within this method and defer
+         * additional computation or storage to another thread to avoid unnecessarily
+         * dropping reports.
+         *
+         * @param window The {@link Window} on which the frame was displayed.
+         * @param frameMetrics the available metrics. This object is reused on every call
+         * and thus <strong>this reference is not valid outside the scope of this method</strong>.
+         * @param dropCountSinceLastInvocation the number of reports dropped since the last time
+         * this callback was invoked.
+         */
+        void onMetricsAvailable(Window window, FrameMetrics frameMetrics,
+                int dropCountSinceLastInvocation);
+    }
+
+
     public Window(Context context) {
         mContext = context;
         mFeatures = mLocalFeatures = getDefaultFeatures(context);
@@ -798,33 +827,28 @@
      * Set an observer to collect frame stats for each frame rendererd in this window.
      *
      * Must be in hardware rendering mode.
-     * @hide
      */
-    public final void addFrameStatsObserver(@NonNull FrameStatsObserver fso) {
+    public final void addFrameMetricsListener(@NonNull FrameMetricsListener listener,
+            Handler handler) {
         final View decorView = getDecorView();
         if (decorView == null) {
             throw new IllegalStateException("can't observe a Window without an attached view");
         }
 
-        if (fso == null) {
-            throw new NullPointerException("FrameStatsObserver cannot be null");
+        if (listener == null) {
+            throw new NullPointerException("listener cannot be null");
         }
 
-        if (fso.isRegistered()) {
-            throw new IllegalStateException("FrameStatsObserver already registered on a Window.");
-        }
-
-        decorView.addFrameStatsObserver(fso);
+        decorView.addFrameMetricsListener(this, listener, handler);
     }
 
     /**
      * Remove observer and stop listening to frame stats for this window.
-     * @hide
      */
-    public final void removeFrameStatsObserver(FrameStatsObserver fso) {
+    public final void removeFrameMetricsListener(FrameMetricsListener listener) {
         final View decorView = getDecorView();
         if (decorView != null) {
-            getDecorView().removeFrameStatsObserver(fso);
+            getDecorView().removeFrameMetricsListener(listener);
         }
     }
 
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
index 43306d0..ec2c05e 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -38,7 +38,6 @@
 import android.util.Slog;
 import android.util.Xml;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
-import android.view.inputmethod.InputMethodSubtypeArray;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -122,7 +121,7 @@
      * @param context The Context in which we are parsing the input method.
      * @param service The ResolveInfo returned from the package manager about
      * this input method's component.
-     * @param additionalSubtypes additional subtypes being added to this InputMethodInfo
+     * @param additionalSubtypesMap additional subtypes being added to this InputMethodInfo
      * @hide
      */
     public InputMethodInfo(Context context, ResolveInfo service,
@@ -429,12 +428,31 @@
         }
     }
 
+    /**
+     * @return {@code true} if the IME is marked to be Encryption-Aware.
+     * @hide
+     */
+    public boolean isEncryptionAware() {
+        if (mService == null || mService.serviceInfo == null ||
+                mService.serviceInfo.applicationInfo == null) {
+            return false;
+        }
+        return mService.serviceInfo.applicationInfo.isEncryptionAware();
+    }
+
     public void dump(Printer pw, String prefix) {
         pw.println(prefix + "mId=" + mId
                 + " mSettingsActivityName=" + mSettingsActivityName
                 + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod);
         pw.println(prefix + "mIsDefaultResId=0x"
                 + Integer.toHexString(mIsDefaultResId));
+        if (mService != null && mService.serviceInfo != null &&
+                mService.serviceInfo.applicationInfo != null) {
+            pw.println(" encryptionAware=" +
+                    mService.serviceInfo.applicationInfo.isEncryptionAware());
+        } else {
+            pw.println(" encryptionAware=unknown");
+        }
         pw.println(prefix + "Service:");
         mService.dump(pw, prefix + "  ");
     }
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index acd0501..dd0e456 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -41,6 +41,7 @@
 #include <Animator.h>
 #include <AnimationContext.h>
 #include <FrameInfo.h>
+#include <FrameMetricsObserver.h>
 #include <IContextFactory.h>
 #include <JankTracker.h>
 #include <RenderNode.h>
@@ -56,10 +57,11 @@
 using namespace android::uirenderer::renderthread;
 
 struct {
-    jfieldID buffer;
+    jfieldID frameMetrics;
+    jfieldID timingDataBuffer;
     jfieldID messageQueue;
-    jmethodID notifyData;
-} gFrameStatsObserverClassInfo;
+    jmethodID callback;
+} gFrameMetricsObserverClassInfo;
 
 static JNIEnv* getenv(JavaVM* vm) {
     JNIEnv* env;
@@ -239,31 +241,46 @@
         mBuffer = buffer;
     }
 
+    void setDropCount(int dropCount) {
+        mDropCount = dropCount;
+    }
+
     virtual void handleMessage(const Message& message);
 
 private:
     JavaVM* mVm;
 
     sp<ObserverProxy> mObserver;
-    BufferPool::Buffer* mBuffer;
+    BufferPool::Buffer* mBuffer = nullptr;
+    int mDropCount = 0;
 };
 
-class ObserverProxy : public FrameStatsObserver {
+static jlongArray get_metrics_buffer(JNIEnv* env, jobject observer) {
+    jobject frameMetrics = env->GetObjectField(
+            observer, gFrameMetricsObserverClassInfo.frameMetrics);
+    LOG_ALWAYS_FATAL_IF(frameMetrics == nullptr, "unable to retrieve data sink object");
+    jobject buffer = env->GetObjectField(
+            frameMetrics, gFrameMetricsObserverClassInfo.timingDataBuffer);
+    LOG_ALWAYS_FATAL_IF(buffer == nullptr, "unable to retrieve data sink buffer");
+    return reinterpret_cast<jlongArray>(buffer);
+}
+
+class ObserverProxy : public FrameMetricsObserver {
 public:
-    ObserverProxy(JavaVM *vm, jobject fso) : mVm(vm) {
+    ObserverProxy(JavaVM *vm, jobject observer) : mVm(vm) {
         JNIEnv* env = getenv(mVm);
 
-        jlongArray longArrayLocal = env->NewLongArray(kBufferSize);
-        LOG_ALWAYS_FATAL_IF(longArrayLocal == nullptr,
-                "OOM: can't allocate frame stats buffer");
-        env->SetObjectField(fso, gFrameStatsObserverClassInfo.buffer, longArrayLocal);
-
-        mFsoWeak = env->NewWeakGlobalRef(fso);
-        LOG_ALWAYS_FATAL_IF(mFsoWeak == nullptr,
+        mObserverWeak = env->NewWeakGlobalRef(observer);
+        LOG_ALWAYS_FATAL_IF(mObserverWeak == nullptr,
                 "unable to create frame stats observer reference");
 
-        jobject messageQueueLocal =
-                env->GetObjectField(fso, gFrameStatsObserverClassInfo.messageQueue);
+        jlongArray buffer = get_metrics_buffer(env, observer);
+        jsize bufferSize = env->GetArrayLength(reinterpret_cast<jarray>(buffer));
+        LOG_ALWAYS_FATAL_IF(bufferSize != kBufferSize,
+                "Mismatched Java/Native FrameMetrics data format.");
+
+        jobject messageQueueLocal = env->GetObjectField(
+                observer, gFrameMetricsObserverClassInfo.messageQueue);
         mMessageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueLocal);
         LOG_ALWAYS_FATAL_IF(mMessageQueue == nullptr, "message queue not available");
 
@@ -274,17 +291,18 @@
 
     ~ObserverProxy() {
         JNIEnv* env = getenv(mVm);
-        env->DeleteWeakGlobalRef(mFsoWeak);
+        env->DeleteWeakGlobalRef(mObserverWeak);
     }
 
-    jweak getJavaObjectRef() {
-        return mFsoWeak;
+    jweak getObserverReference() {
+        return mObserverWeak;
     }
 
-    virtual void notify(BufferPool::Buffer* buffer) {
+    virtual void notify(BufferPool::Buffer* buffer, int dropCount) {
         buffer->incRef();
         mMessageHandler->setBuffer(buffer);
         mMessageHandler->setObserver(this);
+        mMessageHandler->setDropCount(dropCount);
         mMessageQueue->getLooper()->sendMessage(mMessageHandler, mMessage);
     }
 
@@ -292,26 +310,27 @@
     static const int kBufferSize = static_cast<int>(FrameInfoIndex::NumIndexes);
 
     JavaVM* mVm;
-    jweak mFsoWeak;
+    jweak mObserverWeak;
+    jobject mJavaBufferGlobal;
 
     sp<MessageQueue> mMessageQueue;
     sp<NotifyHandler> mMessageHandler;
     Message mMessage;
+
 };
 
 void NotifyHandler::handleMessage(const Message& message) {
     JNIEnv* env = getenv(mVm);
 
-    jobject target = env->NewLocalRef(mObserver->getJavaObjectRef());
+    jobject target = env->NewLocalRef(mObserver->getObserverReference());
 
     if (target != nullptr) {
-        jobject javaBuffer = env->GetObjectField(target, gFrameStatsObserverClassInfo.buffer);
-        if (javaBuffer != nullptr) {
-            env->SetLongArrayRegion(reinterpret_cast<jlongArray>(javaBuffer),
-                    0, mBuffer->getSize(), mBuffer->getBuffer());
-            env->CallVoidMethod(target, gFrameStatsObserverClassInfo.notifyData);
-            env->DeleteLocalRef(target);
-        }
+        jlongArray javaBuffer = get_metrics_buffer(env, target);
+        env->SetLongArrayRegion(javaBuffer,
+                0, mBuffer->getSize(), mBuffer->getBuffer());
+        env->CallVoidMethod(target, gFrameMetricsObserverClassInfo.callback,
+                mDropCount);
+        env->DeleteLocalRef(target);
     }
 
     mBuffer->release();
@@ -579,10 +598,10 @@
 }
 
 // ----------------------------------------------------------------------------
-// FrameStatsObserver
+// FrameMetricsObserver
 // ----------------------------------------------------------------------------
 
-static jlong android_view_ThreadedRenderer_addFrameStatsObserver(JNIEnv* env,
+static jlong android_view_ThreadedRenderer_addFrameMetricsObserver(JNIEnv* env,
         jclass clazz, jlong proxyPtr, jobject fso) {
     JavaVM* vm = nullptr;
     if (env->GetJavaVM(&vm) != JNI_OK) {
@@ -593,25 +612,18 @@
     renderthread::RenderProxy* renderProxy =
             reinterpret_cast<renderthread::RenderProxy*>(proxyPtr);
 
-    FrameStatsObserver* observer = new ObserverProxy(vm, fso);
-    renderProxy->addFrameStatsObserver(observer);
+    FrameMetricsObserver* observer = new ObserverProxy(vm, fso);
+    renderProxy->addFrameMetricsObserver(observer);
     return reinterpret_cast<jlong>(observer);
 }
 
-static void android_view_ThreadedRenderer_removeFrameStatsObserver(JNIEnv* env, jclass clazz,
+static void android_view_ThreadedRenderer_removeFrameMetricsObserver(JNIEnv* env, jclass clazz,
         jlong proxyPtr, jlong observerPtr) {
-    FrameStatsObserver* observer = reinterpret_cast<FrameStatsObserver*>(observerPtr);
+    FrameMetricsObserver* observer = reinterpret_cast<FrameMetricsObserver*>(observerPtr);
     renderthread::RenderProxy* renderProxy =
             reinterpret_cast<renderthread::RenderProxy*>(proxyPtr);
 
-    renderProxy->removeFrameStatsObserver(observer);
-}
-
-static jint android_view_ThreadedRenderer_getDroppedFrameReportCount(JNIEnv* env, jclass clazz,
-        jlong proxyPtr) {
-    renderthread::RenderProxy* renderProxy =
-            reinterpret_cast<renderthread::RenderProxy*>(proxyPtr);
-    return renderProxy->getDroppedFrameReportCount();
+    renderProxy->removeFrameMetricsObserver(observer);
 }
 
 // ----------------------------------------------------------------------------
@@ -684,25 +696,26 @@
     { "nRemoveRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_removeRenderNode},
     { "nDrawRenderNode", "(JJ)V", (void*) android_view_ThreadedRendererd_drawRenderNode},
     { "nSetContentDrawBounds", "(JIIII)V", (void*)android_view_ThreadedRenderer_setContentDrawBounds},
-    { "nAddFrameStatsObserver",
-            "(JLandroid/view/FrameStatsObserver;)J",
-            (void*)android_view_ThreadedRenderer_addFrameStatsObserver },
-    { "nRemoveFrameStatsObserver",
+    { "nAddFrameMetricsObserver",
+            "(JLandroid/view/FrameMetricsObserver;)J",
+            (void*)android_view_ThreadedRenderer_addFrameMetricsObserver },
+    { "nRemoveFrameMetricsObserver",
             "(JJ)V",
-            (void*)android_view_ThreadedRenderer_removeFrameStatsObserver },
-    { "nGetDroppedFrameReportCount",
-            "(J)J",
-            (void*)android_view_ThreadedRenderer_getDroppedFrameReportCount },
+            (void*)android_view_ThreadedRenderer_removeFrameMetricsObserver },
 };
 
 int register_android_view_ThreadedRenderer(JNIEnv* env) {
-    jclass clazz = FindClassOrDie(env, "android/view/FrameStatsObserver");
-    gFrameStatsObserverClassInfo.messageQueue  =
-            GetFieldIDOrDie(env, clazz, "mMessageQueue", "Landroid/os/MessageQueue;");
-    gFrameStatsObserverClassInfo.buffer =
-            GetFieldIDOrDie(env, clazz, "mBuffer", "[J");
-    gFrameStatsObserverClassInfo.notifyData =
-            GetMethodIDOrDie(env, clazz, "notifyDataAvailable", "()V");
+    jclass observerClass = FindClassOrDie(env, "android/view/FrameMetricsObserver");
+    gFrameMetricsObserverClassInfo.frameMetrics = GetFieldIDOrDie(
+            env, observerClass, "mFrameMetrics", "Landroid/view/FrameMetrics;");
+    gFrameMetricsObserverClassInfo.messageQueue = GetFieldIDOrDie(
+            env, observerClass, "mMessageQueue", "Landroid/os/MessageQueue;");
+    gFrameMetricsObserverClassInfo.callback = GetMethodIDOrDie(
+            env, observerClass, "notifyDataAvailable", "(I)V");
+
+    jclass metricsClass = FindClassOrDie(env, "android/view/FrameMetrics");
+    gFrameMetricsObserverClassInfo.timingDataBuffer = GetFieldIDOrDie(
+            env, metricsClass, "mTimingData", "[J");
 
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1db75e6..99daab4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -425,6 +425,8 @@
 
     <protected-broadcast android:name="android.intent.action.DYNAMIC_SENSOR_CHANGED" />
 
+    <protected-broadcast android:name="android.intent.action.ACTION_RADIO_OFF" />
+
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 3a5336c..1496d09 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -429,8 +429,10 @@
          sets. -->
     <attr name="multiArch" format ="boolean" />
 
-    <!-- Specify abiOverride for multiArch application. -->
-    <attr name="abiOverride" />
+    <!-- Specify whether the 32 bit version of the ABI should be used in a
+         multiArch application. If both abioverride flag (i.e. using abi option of abd install)
+         and use32bitAbi are used, then use32bit is ignored.-->
+    <attr name="use32bitAbi" />
 
     <!-- Specify whether a component is allowed to have multiple instances
          of itself running in different processes.  Use with the activity
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 5c5aff0..69d005c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2696,7 +2696,7 @@
     <public type="attr" name="endX" />
     <public type="attr" name="endY" />
     <public type="attr" name="offset" />
-    <public type="attr" name="abiOverride" />
+    <public type="attr" name="use32bitAbi" />
     <public type="attr" name="bitmap" />
     <public type="attr" name="hotSpotX" />
     <public type="attr" name="hotSpotY" />
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
index d133a12..93581db 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
@@ -20,11 +20,13 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.res.Configuration;
 import android.os.Parcel;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.LocaleList;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
 import android.view.inputmethod.InputMethodSubtype;
@@ -230,7 +232,10 @@
                     "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
                     subtypes);
             final ArrayList<InputMethodSubtype> result =
-                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_EN_US, imi);
+                    InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+                            createTargetContextWithLocales(new LocaleList(LOCALE_EN_US))
+                                    .getResources(),
+                            imi);
             assertEquals(1, result.size());
             verifyEquality(autoSubtype, result.get(0));
         }
@@ -251,8 +256,10 @@
                     "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
                     subtypes);
             final ArrayList<InputMethodSubtype> result =
-                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_EN_US, imi);
-            assertEquals(1, result.size());
+                    InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+                            createTargetContextWithLocales(new LocaleList(LOCALE_EN_US))
+                                    .getResources(),
+                            imi);
             verifyEquality(nonAutoEnUS, result.get(0));
         }
 
@@ -271,7 +278,10 @@
                     "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
                     subtypes);
             final ArrayList<InputMethodSubtype> result =
-                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_EN_GB, imi);
+                    InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+                            createTargetContextWithLocales(new LocaleList(LOCALE_EN_GB))
+                                    .getResources(),
+                            imi);
             assertEquals(1, result.size());
             verifyEquality(nonAutoEnGB, result.get(0));
         }
@@ -292,7 +302,10 @@
                     "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
                     subtypes);
             final ArrayList<InputMethodSubtype> result =
-                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FR, imi);
+                    InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+                            createTargetContextWithLocales(new LocaleList(LOCALE_FR))
+                                    .getResources(),
+                            imi);
             assertEquals(1, result.size());
             verifyEquality(nonAutoFrCA, result.get(0));
         }
@@ -309,7 +322,10 @@
                     "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
                     subtypes);
             final ArrayList<InputMethodSubtype> result =
-                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FR_CA, imi);
+                    InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+                            createTargetContextWithLocales(new LocaleList(LOCALE_FR_CA))
+                                    .getResources(),
+                            imi);
             assertEquals(1, result.size());
             verifyEquality(nonAutoFrCA, result.get(0));
         }
@@ -327,7 +343,10 @@
                     "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
                     subtypes);
             final ArrayList<InputMethodSubtype> result =
-                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_JA_JP, imi);
+                    InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+                            createTargetContextWithLocales(new LocaleList(LOCALE_JA_JP))
+                                    .getResources(),
+                            imi);
             assertEquals(3, result.size());
             verifyEquality(nonAutoJa, result.get(0));
             verifyEquality(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, result.get(1));
@@ -344,7 +363,10 @@
                     "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
                     subtypes);
             final ArrayList<InputMethodSubtype> result =
-                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FIL_PH, imi);
+                    InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+                            createTargetContextWithLocales(new LocaleList(LOCALE_FIL_PH))
+                                    .getResources(),
+                            imi);
             assertEquals(1, result.size());
             verifyEquality(nonAutoFil, result.get(0));
         }
@@ -361,7 +383,10 @@
                     "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
                     subtypes);
             final ArrayList<InputMethodSubtype> result =
-                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FI, imi);
+                    InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+                            createTargetContextWithLocales(new LocaleList(LOCALE_FI))
+                                    .getResources(),
+                            imi);
             assertEquals(1, result.size());
             verifyEquality(nonAutoJa, result.get(0));
         }
@@ -376,7 +401,10 @@
                     "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
                     subtypes);
             final ArrayList<InputMethodSubtype> result =
-                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_IN, imi);
+                    InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+                            createTargetContextWithLocales(new LocaleList(LOCALE_IN))
+                                    .getResources(),
+                            imi);
             assertEquals(1, result.size());
             verifyEquality(nonAutoIn, result.get(0));
         }
@@ -389,7 +417,10 @@
                     "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
                     subtypes);
             final ArrayList<InputMethodSubtype> result =
-                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_ID, imi);
+                    InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+                            createTargetContextWithLocales(new LocaleList(LOCALE_ID))
+                                    .getResources(),
+                            imi);
             assertEquals(1, result.size());
             verifyEquality(nonAutoIn, result.get(0));
         }
@@ -402,7 +433,10 @@
                     "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
                     subtypes);
             final ArrayList<InputMethodSubtype> result =
-                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_IN, imi);
+                    InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+                            createTargetContextWithLocales(new LocaleList(LOCALE_IN))
+                                    .getResources(),
+                            imi);
             assertEquals(1, result.size());
             verifyEquality(nonAutoId, result.get(0));
         }
@@ -415,7 +449,10 @@
                     "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
                     subtypes);
             final ArrayList<InputMethodSubtype> result =
-                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_ID, imi);
+                    InputMethodUtils.getImplicitlyApplicableSubtypesLocked(
+                            createTargetContextWithLocales(new LocaleList(LOCALE_ID))
+                                    .getResources(),
+                            imi);
             assertEquals(1, result.size());
             verifyEquality(nonAutoId, result.get(0));
         }
@@ -568,24 +605,11 @@
         }
     }
 
-    private ArrayList<InputMethodSubtype> callGetImplicitlyApplicableSubtypesLockedWithLocale(
-            final Locale locale, final InputMethodInfo imi) {
-        final Context context = getInstrumentation().getTargetContext();
-        final Locale initialLocale = context.getResources().getConfiguration().locale;
-        try {
-            context.getResources().getConfiguration().setLocale(locale);
-            return InputMethodUtils.getImplicitlyApplicableSubtypesLocked(context.getResources(),
-                    imi);
-        } finally {
-            context.getResources().getConfiguration().setLocale(initialLocale);
-        }
-    }
-
     private void assertDefaultEnabledImes(final ArrayList<InputMethodInfo> preinstalledImes,
             final Locale systemLocale, final boolean isSystemReady, String... expectedImeNames) {
-        final Context context = getInstrumentation().getTargetContext();
-        final String[] actualImeNames = getPackageNames(callGetDefaultEnabledImesWithLocale(
-                context, isSystemReady, preinstalledImes, systemLocale));
+        final Context context = createTargetContextWithLocales(new LocaleList(systemLocale));
+        final String[] actualImeNames = getPackageNames(
+                InputMethodUtils.getDefaultEnabledImes(context, isSystemReady, preinstalledImes));
         assertEquals(expectedImeNames.length, actualImeNames.length);
         for (int i = 0; i < expectedImeNames.length; ++i) {
             assertEquals(expectedImeNames[i], actualImeNames[i]);
@@ -606,16 +630,12 @@
         }
     }
 
-    private static ArrayList<InputMethodInfo> callGetDefaultEnabledImesWithLocale(
-            final Context context, final boolean isSystemReady,
-            final ArrayList<InputMethodInfo> imis, final Locale locale) {
-        final Locale initialLocale = context.getResources().getConfiguration().locale;
-        try {
-            context.getResources().getConfiguration().setLocale(locale);
-            return InputMethodUtils.getDefaultEnabledImes(context, isSystemReady, imis);
-        } finally {
-            context.getResources().getConfiguration().setLocale(initialLocale);
-        }
+    private Context createTargetContextWithLocales(final LocaleList locales) {
+        final Configuration resourceConfiguration = new Configuration();
+        resourceConfiguration.setLocales(locales);
+        return getInstrumentation()
+                .getTargetContext()
+                .createConfigurationContext(resourceConfiguration);
     }
 
     private String[] getPackageNames(final ArrayList<InputMethodInfo> imis) {
diff --git a/libs/hwui/FrameStatsObserver.h b/libs/hwui/FrameMetricsObserver.h
similarity index 86%
rename from libs/hwui/FrameStatsObserver.h
rename to libs/hwui/FrameMetricsObserver.h
index 7abc9f1..2b42a80 100644
--- a/libs/hwui/FrameStatsObserver.h
+++ b/libs/hwui/FrameMetricsObserver.h
@@ -23,9 +23,9 @@
 namespace android {
 namespace uirenderer {
 
-class FrameStatsObserver : public VirtualLightRefBase {
+class FrameMetricsObserver : public VirtualLightRefBase {
 public:
-    virtual void notify(BufferPool::Buffer* buffer);
+    virtual void notify(BufferPool::Buffer* buffer, int dropCount);
 };
 
 }; // namespace uirenderer
diff --git a/libs/hwui/FrameStatsReporter.h b/libs/hwui/FrameMetricsReporter.h
similarity index 83%
rename from libs/hwui/FrameStatsReporter.h
rename to libs/hwui/FrameMetricsReporter.h
index b8a9432..0831d24 100644
--- a/libs/hwui/FrameStatsReporter.h
+++ b/libs/hwui/FrameMetricsReporter.h
@@ -21,7 +21,7 @@
 
 #include "BufferPool.h"
 #include "FrameInfo.h"
-#include "FrameStatsObserver.h"
+#include "FrameMetricsObserver.h"
 
 #include <string.h>
 #include <vector>
@@ -29,18 +29,18 @@
 namespace android {
 namespace uirenderer {
 
-class FrameStatsReporter {
+class FrameMetricsReporter {
 public:
-    FrameStatsReporter() {
+    FrameMetricsReporter() {
         mBufferPool = new BufferPool(kBufferSize, kBufferCount);
         LOG_ALWAYS_FATAL_IF(mBufferPool.get() == nullptr, "OOM: unable to allocate buffer pool");
     }
 
-    void addObserver(FrameStatsObserver* observer) {
+    void addObserver(FrameMetricsObserver* observer) {
         mObservers.push_back(observer);
     }
 
-    bool removeObserver(FrameStatsObserver* observer) {
+    bool removeObserver(FrameMetricsObserver* observer) {
         for (size_t i = 0; i < mObservers.size(); i++) {
             if (mObservers[i].get() == observer) {
                 mObservers.erase(mObservers.begin() + i);
@@ -54,7 +54,7 @@
         return mObservers.size() > 0;
     }
 
-    void reportFrameStats(const int64_t* stats) {
+    void reportFrameMetrics(const int64_t* stats) {
         BufferPool::Buffer* statsBuffer = mBufferPool->acquire();
 
         if (statsBuffer != nullptr) {
@@ -63,11 +63,12 @@
 
             // notify on requested threads
             for (size_t i = 0; i < mObservers.size(); i++) {
-                mObservers[i]->notify(statsBuffer);
+                mObservers[i]->notify(statsBuffer, mDroppedReports);
             }
 
             // drop our reference
             statsBuffer->release();
+            mDroppedReports = 0;
         } else {
             mDroppedReports++;
         }
@@ -79,7 +80,7 @@
     static const size_t kBufferCount = 3;
     static const size_t kBufferSize = static_cast<size_t>(FrameInfoIndex::NumIndexes);
 
-    std::vector< sp<FrameStatsObserver> > mObservers;
+    std::vector< sp<FrameMetricsObserver> > mObservers;
 
     sp<BufferPool> mBufferPool;
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index ea702c0..4f528b1 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -507,8 +507,8 @@
 
     mJankTracker.addFrame(*mCurrentFrameInfo);
     mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
-    if (CC_UNLIKELY(mFrameStatsReporter.get() != nullptr)) {
-        mFrameStatsReporter->reportFrameStats(mCurrentFrameInfo->data());
+    if (CC_UNLIKELY(mFrameMetricsReporter.get() != nullptr)) {
+        mFrameMetricsReporter->reportFrameMetrics(mCurrentFrameInfo->data());
     }
 
     GpuMemoryTracker::onFrameCompleted();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 168166e..1f81970 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -20,7 +20,7 @@
 #include "DamageAccumulator.h"
 #include "FrameInfo.h"
 #include "FrameInfoVisualizer.h"
-#include "FrameStatsReporter.h"
+#include "FrameMetricsReporter.h"
 #include "IContextFactory.h"
 #include "LayerUpdateQueue.h"
 #include "RenderNode.h"
@@ -142,26 +142,26 @@
         return mRenderThread.renderState();
     }
 
-    void addFrameStatsObserver(FrameStatsObserver* observer) {
-        if (mFrameStatsReporter.get() == nullptr) {
-            mFrameStatsReporter.reset(new FrameStatsReporter());
+    void addFrameMetricsObserver(FrameMetricsObserver* observer) {
+        if (mFrameMetricsReporter.get() == nullptr) {
+            mFrameMetricsReporter.reset(new FrameMetricsReporter());
         }
 
-        mFrameStatsReporter->addObserver(observer);
+        mFrameMetricsReporter->addObserver(observer);
     }
 
-    void removeFrameStatsObserver(FrameStatsObserver* observer) {
-        if (mFrameStatsReporter.get() != nullptr) {
-            mFrameStatsReporter->removeObserver(observer);
-            if (!mFrameStatsReporter->hasObservers()) {
-                mFrameStatsReporter.reset(nullptr);
+    void removeFrameMetricsObserver(FrameMetricsObserver* observer) {
+        if (mFrameMetricsReporter.get() != nullptr) {
+            mFrameMetricsReporter->removeObserver(observer);
+            if (!mFrameMetricsReporter->hasObservers()) {
+                mFrameMetricsReporter.reset(nullptr);
             }
         }
     }
 
     long getDroppedFrameReportCount() {
-        if (mFrameStatsReporter.get() != nullptr) {
-            return mFrameStatsReporter->getDroppedReports();
+        if (mFrameMetricsReporter.get() != nullptr) {
+            return mFrameMetricsReporter->getDroppedReports();
         }
 
         return 0;
@@ -215,7 +215,7 @@
     std::string mName;
     JankTracker mJankTracker;
     FrameInfoVisualizer mProfiler;
-    std::unique_ptr<FrameStatsReporter> mFrameStatsReporter;
+    std::unique_ptr<FrameMetricsReporter> mFrameMetricsReporter;
 
     std::set<RenderNode*> mPrefetechedLayers;
 
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 7c6cd7e..04223a7 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -568,17 +568,17 @@
     post(task);
 }
 
-CREATE_BRIDGE2(addFrameStatsObserver, CanvasContext* context,
-        FrameStatsObserver* frameStatsObserver) {
-   args->context->addFrameStatsObserver(args->frameStatsObserver);
+CREATE_BRIDGE2(addFrameMetricsObserver, CanvasContext* context,
+        FrameMetricsObserver* frameStatsObserver) {
+   args->context->addFrameMetricsObserver(args->frameStatsObserver);
    if (args->frameStatsObserver != nullptr) {
        args->frameStatsObserver->decStrong(args->context);
    }
    return nullptr;
 }
 
-void RenderProxy::addFrameStatsObserver(FrameStatsObserver* observer) {
-    SETUP_TASK(addFrameStatsObserver);
+void RenderProxy::addFrameMetricsObserver(FrameMetricsObserver* observer) {
+    SETUP_TASK(addFrameMetricsObserver);
     args->context = mContext;
     args->frameStatsObserver = observer;
     if (observer != nullptr) {
@@ -587,17 +587,17 @@
     post(task);
 }
 
-CREATE_BRIDGE2(removeFrameStatsObserver, CanvasContext* context,
-        FrameStatsObserver* frameStatsObserver) {
-   args->context->removeFrameStatsObserver(args->frameStatsObserver);
+CREATE_BRIDGE2(removeFrameMetricsObserver, CanvasContext* context,
+        FrameMetricsObserver* frameStatsObserver) {
+   args->context->removeFrameMetricsObserver(args->frameStatsObserver);
    if (args->frameStatsObserver != nullptr) {
        args->frameStatsObserver->decStrong(args->context);
    }
    return nullptr;
 }
 
-void RenderProxy::removeFrameStatsObserver(FrameStatsObserver* observer) {
-    SETUP_TASK(removeFrameStatsObserver);
+void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observer) {
+    SETUP_TASK(removeFrameMetricsObserver);
     args->context = mContext;
     args->frameStatsObserver = observer;
     if (observer != nullptr) {
@@ -606,16 +606,6 @@
     post(task);
 }
 
-CREATE_BRIDGE1(getDroppedFrameReportCount, CanvasContext* context) {
-    return (void*) args->context->getDroppedFrameReportCount();
-}
-
-long RenderProxy::getDroppedFrameReportCount() {
-    SETUP_TASK(getDroppedFrameReportCount);
-    args->context = mContext;
-    return (long) postAndWait(task);
-}
-
 void RenderProxy::post(RenderTask* task) {
     mRenderThread.queue(task);
 }
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 178724a..8d65a82 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -29,7 +29,7 @@
 #include <utils/StrongPointer.h>
 
 #include "../Caches.h"
-#include "../FrameStatsObserver.h"
+#include "../FrameMetricsObserver.h"
 #include "../IContextFactory.h"
 #include "CanvasContext.h"
 #include "DrawFrameTask.h"
@@ -113,8 +113,8 @@
     ANDROID_API void drawRenderNode(RenderNode* node);
     ANDROID_API void setContentDrawBounds(int left, int top, int right, int bottom);
 
-    ANDROID_API void addFrameStatsObserver(FrameStatsObserver* observer);
-    ANDROID_API void removeFrameStatsObserver(FrameStatsObserver* observer);
+    ANDROID_API void addFrameMetricsObserver(FrameMetricsObserver* observer);
+    ANDROID_API void removeFrameMetricsObserver(FrameMetricsObserver* observer);
     ANDROID_API long getDroppedFrameReportCount();
 
 private:
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 800b914..e342385 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1014,9 +1014,12 @@
      * Reads audio data from the audio hardware for recording into a byte array.
      * The format specified in the AudioRecord constructor should be
      * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array.
+     * The format can be {@link AudioFormat#ENCODING_PCM_16BIT}, but this is deprecated.
      * @param audioData the array to which the recorded audio data is written.
-     * @param offsetInBytes index in audioData from which the data is written expressed in bytes.
+     * @param offsetInBytes index in audioData to which the data is written expressed in bytes.
+     *        Must not be negative, or cause the data access to go out of bounds of the array.
      * @param sizeInBytes the number of requested bytes.
+     *        Must not be negative, or cause the data access to go out of bounds of the array.
      * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
      *     <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
      *     is read.
@@ -1025,7 +1028,8 @@
      * @return the number of bytes that were read or {@link #ERROR_INVALID_OPERATION}
      *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
      *    the parameters don't resolve to valid data and indexes.
-     *    The number of bytes will not exceed sizeInBytes.
+     *    The number of bytes will be a multiple of the frame size in bytes
+     *    not to exceed sizeInBytes.
      */
     public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
             @ReadMode int readMode) {
@@ -1053,12 +1057,14 @@
      * The format specified in the AudioRecord constructor should be
      * {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array.
      * @param audioData the array to which the recorded audio data is written.
-     * @param offsetInShorts index in audioData from which the data is written expressed in shorts.
+     * @param offsetInShorts index in audioData to which the data is written expressed in shorts.
+     *        Must not be negative, or cause the data access to go out of bounds of the array.
      * @param sizeInShorts the number of requested shorts.
+     *        Must not be negative, or cause the data access to go out of bounds of the array.
      * @return the number of shorts that were read or {@link #ERROR_INVALID_OPERATION}
      *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
      *    the parameters don't resolve to valid data and indexes.
-     *    The number of shorts will not exceed sizeInShorts.
+     *    The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
      */
     public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts) {
         return read(audioData, offsetInShorts, sizeInShorts, READ_BLOCKING);
@@ -1070,7 +1076,9 @@
      * {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array.
      * @param audioData the array to which the recorded audio data is written.
      * @param offsetInShorts index in audioData from which the data is written expressed in shorts.
+     *        Must not be negative, or cause the data access to go out of bounds of the array.
      * @param sizeInShorts the number of requested shorts.
+     *        Must not be negative, or cause the data access to go out of bounds of the array.
      * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
      *     <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
      *     is read.
@@ -1079,7 +1087,7 @@
      * @return the number of shorts that were read or {@link #ERROR_INVALID_OPERATION}
      *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
      *    the parameters don't resolve to valid data and indexes.
-     *    The number of shorts will not exceed sizeInShorts.
+     *    The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
      */
     public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts,
             @ReadMode int readMode) {
@@ -1108,7 +1116,9 @@
      * {@link AudioFormat#ENCODING_PCM_FLOAT} to correspond to the data in the array.
      * @param audioData the array to which the recorded audio data is written.
      * @param offsetInFloats index in audioData from which the data is written.
+     *        Must not be negative, or cause the data access to go out of bounds of the array.
      * @param sizeInFloats the number of requested floats.
+     *        Must not be negative, or cause the data access to go out of bounds of the array.
      * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
      *     <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
      *     is read.
@@ -1117,7 +1127,7 @@
      * @return the number of floats that were read or {@link #ERROR_INVALID_OPERATION}
      *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
      *    the parameters don't resolve to valid data and indexes.
-     *    The number of floats will not exceed sizeInFloats.
+     *    The number of floats will be a multiple of the channel count not to exceed sizeInFloats.
      */
     public int read(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats,
             @ReadMode int readMode) {
@@ -1154,6 +1164,7 @@
      * The representation of the data in the buffer will depend on the format specified in
      * the AudioRecord constructor, and will be native endian.
      * @param audioBuffer the direct buffer to which the recorded audio data is written.
+     * Data is written to audioBuffer.position().
      * @param sizeInBytes the number of requested bytes. It is recommended but not enforced
      *    that the number of bytes requested be a multiple of the frame size (sample size in
      *    bytes multiplied by the channel count).
@@ -1161,7 +1172,7 @@
      *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
      *    the parameters don't resolve to valid data and indexes.
      *    The number of bytes will not exceed sizeInBytes.
-     *    The number of bytes read will truncated to be a multiple of the frame size.
+     *    The number of bytes read will be truncated to be a multiple of the frame size.
      */
     public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes) {
         return read(audioBuffer, sizeInBytes, READ_BLOCKING);
@@ -1175,6 +1186,7 @@
      * The representation of the data in the buffer will depend on the format specified in
      * the AudioRecord constructor, and will be native endian.
      * @param audioBuffer the direct buffer to which the recorded audio data is written.
+     * Data is written to audioBuffer.position().
      * @param sizeInBytes the number of requested bytes. It is recommended but not enforced
      *    that the number of bytes requested be a multiple of the frame size (sample size in
      *    bytes multiplied by the channel count).
@@ -1187,7 +1199,7 @@
      *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
      *    the parameters don't resolve to valid data and indexes.
      *    The number of bytes will not exceed sizeInBytes.
-     *    The number of bytes read will truncated to be a multiple of the frame size.
+     *    The number of bytes read will be truncated to be a multiple of the frame size.
      */
     public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes, @ReadMode int readMode) {
         if (mState != STATE_INITIALIZED) {
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index b26b310..bdf6d9f 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1774,6 +1774,7 @@
      * or copies audio data for later playback (static buffer mode).
      * The format specified in the AudioTrack constructor should be
      * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array.
+     * The format can be {@link AudioFormat#ENCODING_PCM_16BIT}, but this is deprecated.
      * <p>
      * In streaming mode, the write will normally block until all the data has been enqueued for
      * playback, and will return a full transfer count.  However, if the track is stopped or paused
@@ -1786,7 +1787,9 @@
      * @param audioData the array that holds the data to play.
      * @param offsetInBytes the offset expressed in bytes in audioData where the data to write
      *    starts.
+     *    Must not be negative, or cause the data access to go out of bounds of the array.
      * @param sizeInBytes the number of bytes to write in audioData after the offset.
+     *    Must not be negative, or cause the data access to go out of bounds of the array.
      * @return zero or the positive number of bytes that were written, or
      *    {@link #ERROR_INVALID_OPERATION}
      *    if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
@@ -1795,6 +1798,8 @@
      *    needs to be recreated.
      *    The dead object error code is not returned if some data was successfully transferred.
      *    In this case, the error is returned at the next write().
+     *    The number of bytes will be a multiple of the frame size in bytes
+     *    not to exceed sizeInBytes.
      *
      * This is equivalent to {@link #write(byte[], int, int, int)} with <code>writeMode</code>
      * set to  {@link #WRITE_BLOCKING}.
@@ -1808,6 +1813,7 @@
      * or copies audio data for later playback (static buffer mode).
      * The format specified in the AudioTrack constructor should be
      * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array.
+     * The format can be {@link AudioFormat#ENCODING_PCM_16BIT}, but this is deprecated.
      * <p>
      * In streaming mode, the blocking behavior depends on the write mode.  If the write mode is
      * {@link #WRITE_BLOCKING}, the write will normally block until all the data has been enqueued
@@ -1823,7 +1829,9 @@
      * @param audioData the array that holds the data to play.
      * @param offsetInBytes the offset expressed in bytes in audioData where the data to write
      *    starts.
+     *    Must not be negative, or cause the data access to go out of bounds of the array.
      * @param sizeInBytes the number of bytes to write in audioData after the offset.
+     *    Must not be negative, or cause the data access to go out of bounds of the array.
      * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
      *     effect in static mode.
      *     <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
@@ -1838,6 +1846,8 @@
      *    needs to be recreated.
      *    The dead object error code is not returned if some data was successfully transferred.
      *    In this case, the error is returned at the next write().
+     *    The number of bytes will be a multiple of the frame size in bytes
+     *    not to exceed sizeInBytes.
      */
     public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
             @WriteMode int writeMode) {
@@ -1887,7 +1897,9 @@
      * @param audioData the array that holds the data to play.
      * @param offsetInShorts the offset expressed in shorts in audioData where the data to play
      *     starts.
+     *    Must not be negative, or cause the data access to go out of bounds of the array.
      * @param sizeInShorts the number of shorts to read in audioData after the offset.
+     *    Must not be negative, or cause the data access to go out of bounds of the array.
      * @return zero or the positive number of shorts that were written, or
      *    {@link #ERROR_INVALID_OPERATION}
      *    if the track isn't properly initialized, or {@link #ERROR_BAD_VALUE} if
@@ -1896,6 +1908,7 @@
      *    needs to be recreated.
      *    The dead object error code is not returned if some data was successfully transferred.
      *    In this case, the error is returned at the next write().
+     *    The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
      *
      * This is equivalent to {@link #write(short[], int, int, int)} with <code>writeMode</code>
      * set to  {@link #WRITE_BLOCKING}.
@@ -1923,7 +1936,9 @@
      * @param audioData the array that holds the data to write.
      * @param offsetInShorts the offset expressed in shorts in audioData where the data to write
      *     starts.
+     *    Must not be negative, or cause the data access to go out of bounds of the array.
      * @param sizeInShorts the number of shorts to read in audioData after the offset.
+     *    Must not be negative, or cause the data access to go out of bounds of the array.
      * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
      *     effect in static mode.
      *     <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
@@ -1938,6 +1953,7 @@
      *    needs to be recreated.
      *    The dead object error code is not returned if some data was successfully transferred.
      *    In this case, the error is returned at the next write().
+     *    The number of shorts will be a multiple of the channel count not to exceed sizeInShorts.
      */
     public int write(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts,
             @WriteMode int writeMode) {
@@ -1999,7 +2015,9 @@
      *     to provide samples values within the nominal range.
      * @param offsetInFloats the offset, expressed as a number of floats,
      *     in audioData where the data to write starts.
+     *    Must not be negative, or cause the data access to go out of bounds of the array.
      * @param sizeInFloats the number of floats to write in audioData after the offset.
+     *    Must not be negative, or cause the data access to go out of bounds of the array.
      * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
      *     effect in static mode.
      *     <br>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
@@ -2014,6 +2032,7 @@
      *    needs to be recreated.
      *    The dead object error code is not returned if some data was successfully transferred.
      *    In this case, the error is returned at the next write().
+     *    The number of floats will be a multiple of the channel count not to exceed sizeInFloats.
      */
     public int write(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats,
             @WriteMode int writeMode) {
@@ -2075,7 +2094,9 @@
      *     <BR>Note that upon return, the buffer position (<code>audioData.position()</code>) will
      *     have been advanced to reflect the amount of data that was successfully written to
      *     the AudioTrack.
-     * @param sizeInBytes number of bytes to write.
+     * @param sizeInBytes number of bytes to write.  It is recommended but not enforced
+     *     that the number of bytes requested be a multiple of the frame size (sample size in
+     *     bytes multiplied by the channel count).
      *     <BR>Note this may differ from <code>audioData.remaining()</code>, but cannot exceed it.
      * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
      *     effect in static mode.
@@ -2142,7 +2163,9 @@
      *     <BR>Note that upon return, the buffer position (<code>audioData.position()</code>) will
      *     have been advanced to reflect the amount of data that was successfully written to
      *     the AudioTrack.
-     * @param sizeInBytes number of bytes to write.
+     * @param sizeInBytes number of bytes to write.  It is recommended but not enforced
+     *     that the number of bytes requested be a multiple of the frame size (sample size in
+     *     bytes multiplied by the channel count).
      *     <BR>Note this may differ from <code>audioData.remaining()</code>, but cannot exceed it.
      * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}.
      *     <BR>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
index 3faa8f4..6af492c 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/Mapper.java
@@ -73,7 +73,6 @@
                     extraValuesList,
                     COLUMN_PARENT_DOCUMENT_ID + " IS NULL",
                     EMPTY_ARGS,
-                    /* heuristic */ false,
                     COLUMN_DEVICE_ID);
             database.setTransactionSuccessful();
             return changed;
@@ -92,16 +91,13 @@
         final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
         database.beginTransaction();
         try {
-            final boolean heuristic;
             final String mapColumn;
             Preconditions.checkState(mMappingMode.containsKey(parentDocumentId));
             switch (mMappingMode.get(parentDocumentId)) {
                 case MAP_BY_MTP_IDENTIFIER:
-                    heuristic = false;
                     mapColumn = COLUMN_STORAGE_ID;
                     break;
                 case MAP_BY_NAME:
-                    heuristic = true;
                     mapColumn = Document.COLUMN_DISPLAY_NAME;
                     break;
                 default:
@@ -120,7 +116,6 @@
                     extraValuesList,
                     COLUMN_PARENT_DOCUMENT_ID + "=?",
                     strings(parentDocumentId),
-                    heuristic,
                     mapColumn);
 
             database.setTransactionSuccessful();
@@ -137,16 +132,13 @@
      * @param documents List of document information.
      */
     synchronized void putChildDocuments(int deviceId, String parentId, MtpObjectInfo[] documents) {
-        final boolean heuristic;
         final String mapColumn;
         Preconditions.checkState(mMappingMode.containsKey(parentId));
         switch (mMappingMode.get(parentId)) {
             case MAP_BY_MTP_IDENTIFIER:
-                heuristic = false;
                 mapColumn = COLUMN_OBJECT_HANDLE;
                 break;
             case MAP_BY_NAME:
-                heuristic = true;
                 mapColumn = Document.COLUMN_DISPLAY_NAME;
                 break;
             default:
@@ -163,17 +155,13 @@
                 null,
                 COLUMN_PARENT_DOCUMENT_ID + "=?",
                 strings(parentId),
-                heuristic,
                 mapColumn);
     }
 
-    @VisibleForTesting
     void clearMapping() {
         final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
         database.beginTransaction();
         try {
-            mDatabase.deleteDocumentsAndRootsRecursively(
-                    COLUMN_ROW_STATE + " = ?", strings(ROW_STATE_PENDING));
             final ContentValues values = new ContentValues();
             values.putNull(COLUMN_OBJECT_HANDLE);
             values.putNull(COLUMN_STORAGE_ID);
@@ -209,11 +197,6 @@
         final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
         database.beginTransaction();
         try {
-            // Delete all pending rows.
-            mDatabase.deleteDocumentsAndRootsRecursively(
-                    selection + " AND " + COLUMN_ROW_STATE + "=?",
-                    DatabaseUtils.appendSelectionArgs(args, strings(ROW_STATE_PENDING)));
-
             // Set all documents as invalidated.
             final ContentValues values = new ContentValues();
             values.put(COLUMN_ROW_STATE, ROW_STATE_INVALIDATED);
@@ -245,7 +228,6 @@
      * @param rootExtraValuesList Values for root extra to be stored in the database.
      * @param selection SQL where closure to select rows that shares the same parent.
      * @param args Argument for selection SQL.
-     * @param heuristic Whether the mapping mode is heuristic.
      * @return Whether the method adds new rows.
      */
     private boolean putDocuments(
@@ -253,7 +235,6 @@
             @Nullable ContentValues[] rootExtraValuesList,
             String selection,
             String[] args,
-            boolean heuristic,
             String mappingKey) {
         final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
         boolean added = false;
@@ -285,7 +266,7 @@
                     if (candidateCursor.getCount() == 0) {
                         rowId = database.insert(TABLE_DOCUMENTS, null, values);
                         added = true;
-                    } else if (!heuristic) {
+                    } else {
                         candidateCursor.moveToNext();
                         rowId = candidateCursor.getLong(0);
                         database.update(
@@ -293,9 +274,6 @@
                                 values,
                                 SELECTION_DOCUMENT_ID,
                                 strings(rowId));
-                    } else {
-                        values.put(COLUMN_ROW_STATE, ROW_STATE_PENDING);
-                        rowId = database.insertOrThrow(TABLE_DOCUMENTS, null, values);
                     }
                     // Document ID is a primary integer key of the table. So the returned row
                     // IDs should be same with the document ID.
@@ -334,84 +312,10 @@
             selection = COLUMN_PARENT_DOCUMENT_ID + " IS NULL";
             args = EMPTY_ARGS;
         }
-        final String groupKey;
-        switch (mMappingMode.get(parentId)) {
-            case MAP_BY_MTP_IDENTIFIER:
-                groupKey = parentId != null ? COLUMN_OBJECT_HANDLE : COLUMN_STORAGE_ID;
-                break;
-            case MAP_BY_NAME:
-                groupKey = Document.COLUMN_DISPLAY_NAME;
-                break;
-            default:
-                throw new Error("Unexpected mapping state.");
-        }
         mMappingMode.remove(parentId);
         final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
         database.beginTransaction();
         try {
-            // Get 1-to-1 mapping of invalidated document and pending document.
-            final String invalidatedIdQuery = createStateFilter(
-                    ROW_STATE_INVALIDATED, Document.COLUMN_DOCUMENT_ID);
-            final String pendingIdQuery = createStateFilter(
-                    ROW_STATE_PENDING, Document.COLUMN_DOCUMENT_ID);
-            // SQL should be like:
-            // SELECT group_concat(CASE WHEN raw_state = 1 THEN document_id ELSE NULL END),
-            //        group_concat(CASE WHEN raw_state = 2 THEN document_id ELSE NULL END)
-            // WHERE device_id = ? AND parent_document_id IS NULL
-            // GROUP BY display_name
-            // HAVING count(CASE WHEN raw_state = 1 THEN document_id ELSE NULL END) = 1 AND
-            //        count(CASE WHEN raw_state = 2 THEN document_id ELSE NULL END) = 1
-            final Cursor mergingCursor = database.query(
-                    TABLE_DOCUMENTS,
-                    new String[] {
-                            "group_concat(" + invalidatedIdQuery + ")",
-                            "group_concat(" + pendingIdQuery + ")"
-                    },
-                    selection,
-                    args,
-                    groupKey,
-                    "count(" + invalidatedIdQuery + ") = 1 AND count(" + pendingIdQuery + ") = 1",
-                    null);
-
-            final ContentValues values = new ContentValues();
-            while (mergingCursor.moveToNext()) {
-                final String invalidatedId = mergingCursor.getString(0);
-                final String pendingId = mergingCursor.getString(1);
-
-                // Obtain the new values including the latest object handle from mapping row.
-                getFirstRow(
-                        TABLE_DOCUMENTS,
-                        SELECTION_DOCUMENT_ID,
-                        new String[] { pendingId },
-                        values);
-                values.remove(Document.COLUMN_DOCUMENT_ID);
-                values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
-                database.update(
-                        TABLE_DOCUMENTS,
-                        values,
-                        SELECTION_DOCUMENT_ID,
-                        new String[] { invalidatedId });
-
-                getFirstRow(
-                        TABLE_ROOT_EXTRA,
-                        SELECTION_ROOT_ID,
-                        new String[] { pendingId },
-                        values);
-                if (values.size() > 0) {
-                    values.remove(Root.COLUMN_ROOT_ID);
-                    database.update(
-                            TABLE_ROOT_EXTRA,
-                            values,
-                            SELECTION_ROOT_ID,
-                            new String[] { invalidatedId });
-                }
-
-                // Delete 'pending' row.
-                mDatabase.deleteDocumentsAndRootsRecursively(
-                        SELECTION_DOCUMENT_ID, new String[] { pendingId });
-            }
-            mergingCursor.close();
-
             boolean changed = false;
 
             // Delete all invalidated rows that cannot be mapped.
@@ -421,58 +325,10 @@
                 changed = true;
             }
 
-            // The database cannot find old document ID for the pending rows.
-            // Turn the all pending rows into valid state, which means the rows become to be
-            // valid with new document ID.
-            values.clear();
-            values.put(COLUMN_ROW_STATE, ROW_STATE_VALID);
-            if (database.update(
-                    TABLE_DOCUMENTS,
-                    values,
-                    COLUMN_ROW_STATE + " = ? AND " + selection,
-                    DatabaseUtils.appendSelectionArgs(strings(ROW_STATE_PENDING), args)) != 0) {
-                changed = true;
-            }
             database.setTransactionSuccessful();
             return changed;
         } finally {
             database.endTransaction();
         }
     }
-
-    /**
-     * Obtains values of the first row for the query.
-     * @param values ContentValues that the values are stored to.
-     * @param table Target table.
-     * @param selection Query to select rows.
-     * @param args Argument for query.
-     */
-    private void getFirstRow(String table, String selection, String[] args, ContentValues values) {
-        final SQLiteDatabase database = mDatabase.getSQLiteDatabase();
-        values.clear();
-        final Cursor cursor = database.query(table, null, selection, args, null, null, null, "1");
-        try {
-            if (cursor.getCount() == 0) {
-                return;
-            }
-            cursor.moveToNext();
-            DatabaseUtils.cursorRowToContentValues(cursor, values);
-        } finally {
-            cursor.close();
-        }
-    }
-
-    /**
-     * Gets SQL expression that represents the given value or NULL depends on the row state.
-     * You must pass static constants to this methods otherwise you may be suffered from SQL
-     * injections.
-     * @param state Expected row state.
-     * @param a SQL value.
-     * @return Expression that represents a if the row state is expected one, and represents NULL
-     *     otherwise.
-     */
-    private static String createStateFilter(int state, String a) {
-        return "CASE WHEN " + COLUMN_ROW_STATE + " = " + Integer.toString(state) +
-                " THEN " + a + " ELSE NULL END";
-    }
 }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
index 3cfb82f..33687cb 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java
@@ -78,14 +78,6 @@
     static final int ROW_STATE_INVALIDATED = 1;
 
     /**
-     * The state represents the raw has a valid object handle but it may be going to be mapped with
-     * another rows invalidated. After fetching all documents under the parent, the database tries
-     * to map the pending documents and the invalidated documents in order to keep old document ID
-     * alive.
-     */
-    static final int ROW_STATE_PENDING = 2;
-
-    /**
      * Mapping mode that uses MTP identifier to find corresponding rows.
      */
     static final int MAP_BY_MTP_IDENTIFIER = 0;
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index a49dc67..a75012e 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -310,14 +310,14 @@
             assertEquals(3, cursor.getCount());
             cursor.moveToNext();
             assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
-            assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
+            assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
             assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.moveToNext();
             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertTrue(isNull(cursor, COLUMN_STORAGE_ID));
             assertEquals("Storage B", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.moveToNext();
-            assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
+            assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
             assertEquals("Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
@@ -333,7 +333,7 @@
             assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
             assertEquals("Storage A", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.moveToNext();
-            assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
+            assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(202, getInt(cursor, COLUMN_STORAGE_ID));
             assertEquals("Storage C", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
@@ -387,7 +387,7 @@
             assertEquals(4, cursor.getCount());
 
             cursor.moveToPosition(3);
-            assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID));
+            assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(203, getInt(cursor, COLUMN_OBJECT_HANDLE));
             assertEquals("video.mp4", getString(cursor, COLUMN_DISPLAY_NAME));
 
@@ -406,7 +406,7 @@
             assertEquals("note.txt", getString(cursor, COLUMN_DISPLAY_NAME));
 
             cursor.moveToNext();
-            assertEquals(5, getInt(cursor, COLUMN_DOCUMENT_ID));
+            assertEquals(4, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(203, getInt(cursor, COLUMN_OBJECT_HANDLE));
             assertEquals("video.mp4", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
@@ -544,7 +544,7 @@
             assertEquals(1, cursor.getCount());
             cursor.moveToNext();
             assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
-            assertTrue(isNull(cursor, COLUMN_OBJECT_HANDLE));
+            assertEquals(201, getInt(cursor, COLUMN_OBJECT_HANDLE));
             cursor.close();
         }
     }
@@ -626,11 +626,12 @@
             final Cursor cursor = mDatabase.queryRootDocuments(columns);
             assertEquals(2, cursor.getCount());
             cursor.moveToNext();
-            assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
+            // One reuses exisitng document ID 1.
+            assertEquals(1, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(200, getInt(cursor, COLUMN_STORAGE_ID));
             assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.moveToNext();
-            assertEquals(3, getInt(cursor, COLUMN_DOCUMENT_ID));
+            assertEquals(2, getInt(cursor, COLUMN_DOCUMENT_ID));
             assertEquals(201, getInt(cursor, COLUMN_STORAGE_ID));
             assertEquals("Storage", getString(cursor, COLUMN_DISPLAY_NAME));
             cursor.close();
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index e5e5710..6702cef 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -282,9 +282,6 @@
     <!-- The padding between freeform workspace tasks -->
     <dimen name="recents_freeform_workspace_task_padding">8dp</dimen>
 
-    <!-- Space reserved for the cards behind the top card in the top stack -->
-    <dimen name="top_stack_peek_amount">12dp</dimen>
-
     <!-- Space reserved for the cards behind the top card in the bottom stack -->
     <dimen name="bottom_stack_peek_amount">12dp</dimen>
 
@@ -295,9 +292,6 @@
     <!-- The height of the area before the bottom stack in which the notifications slow down -->
     <dimen name="bottom_stack_slow_down_length">12dp</dimen>
 
-    <!-- The height of the area before the top stack in which the notifications slow down -->
-    <dimen name="top_stack_slow_down_length">12dp</dimen>
-
     <!-- Z distance between notifications if they are in the stack -->
     <dimen name="z_distance_between_notifications">0.5dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 7f1316f..84b2031 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -947,6 +947,10 @@
         }
     }
 
+    public boolean mustStayOnScreen() {
+        return mIsHeadsUp;
+    }
+
     private void updateClearability() {
         // public versions cannot be dismissed
         mVetoButton.setVisibility(isClearable() && !mShowingPublic ? View.VISIBLE : View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index a0fb34a..8042b60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -399,6 +399,10 @@
         return false;
     }
 
+    public boolean mustStayOnScreen() {
+        return false;
+    }
+
     /**
      * A listener notifying when {@link #getActualHeight} changes.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
index b5b7f43..79c21f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java
@@ -79,7 +79,8 @@
                 mTouchingHeadsUpView = false;
                 if (child instanceof ExpandableNotificationRow) {
                     mPickedChild = (ExpandableNotificationRow) child;
-                    mTouchingHeadsUpView = mPickedChild.isHeadsUp() && mPickedChild.isPinned();
+                    mTouchingHeadsUpView = !mStackScroller.isExpanded()
+                            && mPickedChild.isHeadsUp() && mPickedChild.isPinned();
                 }
                 break;
             case MotionEvent.ACTION_POINTER_UP:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 5f5974e..0febbd2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -738,9 +738,9 @@
                 + (offscreen ? " OFFSCREEN!" : ""));
 
         pw.println(String.format("      mCurrentView: id=%s (%dx%d) %s",
-                        getResourceName(mCurrentView.getId()),
-                        mCurrentView.getWidth(), mCurrentView.getHeight(),
-                        visibilityToString(mCurrentView.getVisibility())));
+                        getResourceName(getCurrentView().getId()),
+                        getCurrentView().getWidth(), getCurrentView().getHeight(),
+                        visibilityToString(getCurrentView().getVisibility())));
 
         pw.println(String.format("      disabled=0x%08x vertical=%s menu=%s",
                         mDisabledFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 09a7bf0..50a49a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -519,7 +519,7 @@
      */
     protected boolean mStartedGoingToSleep;
 
-    private static final int VISIBLE_LOCATIONS = StackViewState.LOCATION_FIRST_CARD
+    private static final int VISIBLE_LOCATIONS = StackViewState.LOCATION_FIRST_HUN
             | StackViewState.LOCATION_MAIN_AREA;
 
     private final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index cc0e67d..49e9c3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -75,7 +75,7 @@
         ExpandableView.OnHeightChangedListener, NotificationGroupManager.OnGroupChangeListener {
 
     public static final float BACKGROUND_ALPHA_DIMMED = 0.7f;
-    private static final String TAG = "NotificationStackScrollLayout";
+    private static final String TAG = "StackScroller";
     private static final boolean DEBUG = false;
     private static final float RUBBER_BAND_FACTOR_NORMAL = 0.35f;
     private static final float RUBBER_BAND_FACTOR_AFTER_EXPAND = 0.15f;
@@ -136,7 +136,7 @@
     private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
     private AmbientState mAmbientState = new AmbientState();
     private NotificationGroupManager mGroupManager;
-    private ArrayList<View> mChildrenToAddAnimated = new ArrayList<>();
+    private HashSet<View> mChildrenToAddAnimated = new HashSet<>();
     private ArrayList<View> mAddedHeadsUpChildren = new ArrayList<>();
     private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<>();
     private ArrayList<View> mSnappedBackChildren = new ArrayList<>();
@@ -474,6 +474,7 @@
      * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
      */
     private void updateChildren() {
+        updateScrollStateForAddedChildren();
         mAmbientState.setScrollY(mOwnScrollY);
         mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
         if (!isCurrentlyAnimating() && !mNeedsAnimation) {
@@ -483,6 +484,28 @@
         }
     }
 
+    private void updateScrollStateForAddedChildren() {
+        if (mChildrenToAddAnimated.isEmpty()) {
+            return;
+        }
+        for (int i = 0; i < getChildCount(); i++) {
+            ExpandableView child = (ExpandableView) getChildAt(i);
+            if (mChildrenToAddAnimated.contains(child)) {
+                int startingPosition = getPositionInLinearLayout(child);
+                int padding = child.needsIncreasedPadding()
+                        ? mIncreasedPaddingBetweenElements :
+                        mPaddingBetweenElements;
+                int childHeight = getIntrinsicHeight(child) + padding;
+                if (startingPosition < mOwnScrollY) {
+                    // This child starts off screen, so let's keep it offscreen to keep the others visible
+
+                    mOwnScrollY += childHeight;
+                }
+            }
+        }
+        clampScrollPosition();
+    }
+
     private void requestChildrenUpdate() {
         if (!mChildrenUpdateRequested) {
             getViewTreeObserver().addOnPreDrawListener(mChildrenUpdater);
@@ -1648,12 +1671,17 @@
                 bottom = (int) (lastView.getTranslationY() + lastView.getActualHeight());
                 bottom = Math.min(bottom, getHeight());
             }
-        } else if (mPhoneStatusBar.getBarState() == StatusBarState.KEYGUARD) {
-            top = mTopPadding;
+        } else {
+            top = (int) (mTopPadding + mStackTranslation);
             bottom = top;
         }
-        mBackgroundBounds.top = Math.max(0, top);
-        mBackgroundBounds.bottom = Math.min(getHeight(), bottom);
+        if (mPhoneStatusBar.getBarState() != StatusBarState.KEYGUARD) {
+            mBackgroundBounds.top = (int) Math.max(mTopPadding + mStackTranslation, top);
+        } else {
+            // otherwise the animation from the shade to the keyguard will jump as it's maxed
+            mBackgroundBounds.top = Math.max(0, top);
+        }
+        mBackgroundBounds.bottom = Math.min(getHeight(), Math.max(bottom, top));
     }
 
     private ActivatableNotificationView getFirstPinnedHeadsUp() {
@@ -3206,6 +3234,10 @@
         }
     }
 
+    public boolean isExpanded() {
+        return mIsExpanded;
+    }
+
     /**
      * A listener that is notified when some child locations might have changed.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index f6959f0..e87b363 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -39,17 +39,14 @@
     private static final String LOG_TAG = "StackScrollAlgorithm";
 
     private static final int MAX_ITEMS_IN_BOTTOM_STACK = 3;
-    private static final int MAX_ITEMS_IN_TOP_STACK = 3;
 
     private int mPaddingBetweenElements;
     private int mIncreasedPaddingBetweenElements;
     private int mCollapsedSize;
-    private int mTopStackPeekSize;
     private int mBottomStackPeekSize;
     private int mZDistanceBetweenElements;
     private int mZBasicHeight;
 
-    private StackIndentationFunctor mTopStackIndentationFunctor;
     private StackIndentationFunctor mBottomStackIndentationFunctor;
 
     private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState();
@@ -58,12 +55,8 @@
     private boolean mIsExpanded;
     private ExpandableView mFirstChildWhileExpanding;
     private boolean mExpandedOnStart;
-    private int mTopStackTotalSize;
     private int mBottomStackSlowDownLength;
-    private int mTopStackSlowDownLength;
     private int mCollapseSecondCardPadding;
-    private ExpandableView mFirstChild;
-    private int mFirstChildMinHeight;
 
     public StackScrollAlgorithm(Context context) {
         initView(context);
@@ -71,22 +64,6 @@
 
     public void initView(Context context) {
         initConstants(context);
-        updatePadding();
-    }
-
-    private void updatePadding() {
-        mTopStackTotalSize = mTopStackSlowDownLength + mPaddingBetweenElements
-                + mTopStackPeekSize;
-        mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
-                MAX_ITEMS_IN_TOP_STACK,
-                mTopStackPeekSize,
-                mTopStackTotalSize - mTopStackPeekSize,
-                0.5f);
-        mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
-                MAX_ITEMS_IN_BOTTOM_STACK,
-                mBottomStackPeekSize,
-                getBottomStackSlowDownLength(),
-                0.5f);
     }
 
     public int getBottomStackSlowDownLength() {
@@ -100,8 +77,6 @@
                 .getDimensionPixelSize(R.dimen.notification_divider_height_increased);
         mCollapsedSize = context.getResources()
                 .getDimensionPixelSize(R.dimen.notification_min_height);
-        mTopStackPeekSize = context.getResources()
-                .getDimensionPixelSize(R.dimen.top_stack_peek_amount);
         mBottomStackPeekSize = context.getResources()
                 .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
         mZDistanceBetweenElements = Math.max(1, context.getResources()
@@ -109,10 +84,13 @@
         mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
         mBottomStackSlowDownLength = context.getResources()
                 .getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length);
-        mTopStackSlowDownLength = context.getResources()
-                .getDimensionPixelSize(R.dimen.top_stack_slow_down_length);
         mCollapseSecondCardPadding = context.getResources().getDimensionPixelSize(
                 R.dimen.notification_collapse_second_card_padding);
+        mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
+                MAX_ITEMS_IN_BOTTOM_STACK,
+                mBottomStackPeekSize,
+                getBottomStackSlowDownLength(),
+                0.5f);
     }
 
     public void getStackScrollState(AmbientState ambientState, StackScrollState resultState) {
@@ -123,32 +101,13 @@
         // First we reset the view states to their default values.
         resultState.resetViewStates();
 
-        algorithmState.itemsInTopStack = 0.0f;
-        algorithmState.partialInTop = 0.0f;
-        algorithmState.lastTopStackIndex = 0;
-        algorithmState.scrolledPixelsTop = 0;
-        algorithmState.itemsInBottomStack = 0.0f;
-        algorithmState.partialInBottom = 0.0f;
-        mFirstChildMinHeight = mFirstChild == null ? 0 : mFirstChild.getMinHeight();
-        float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
+        initAlgorithmState(resultState, algorithmState, ambientState);
 
-        int scrollY = ambientState.getScrollY();
-
-        // Due to the overScroller, the stackscroller can have negative scroll state. This is
-        // already accounted for by the top padding and doesn't need an additional adaption
-        scrollY = Math.max(0, scrollY);
-        algorithmState.scrollY = (int) (scrollY + mFirstChildMinHeight + bottomOverScroll);
-
-        initAlgorithmState(resultState, algorithmState);
-
-        // Phase 1:
-        findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState, ambientState);
-
-        // Phase 2:
         updatePositionsForState(resultState, algorithmState, ambientState);
 
-        // Phase 3:
-        updateZValuesForState(resultState, algorithmState);
+        updateZValuesForState(resultState, algorithmState, ambientState);
+
+        updateHeadsUpStates(resultState, algorithmState, ambientState);
 
         handleDraggedViews(ambientState, resultState, algorithmState);
         updateDimmedActivatedHideSensitive(ambientState, resultState, algorithmState);
@@ -185,6 +144,7 @@
     private void updateClipping(StackScrollState resultState,
             StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
         boolean dismissAllInProgress = ambientState.isDismissAllInProgress();
+        float drawStart = ambientState.getTopPadding() + ambientState.getStackTranslation();
         float previousNotificationEnd = 0;
         float previousNotificationStart = 0;
         boolean previousNotificationIsSwiped = false;
@@ -192,6 +152,10 @@
         for (int i = 0; i < childCount; i++) {
             ExpandableView child = algorithmState.visibleChildren.get(i);
             StackViewState state = resultState.getViewStateForView(child);
+            if (!child.mustStayOnScreen()) {
+                previousNotificationEnd = Math.max(drawStart, previousNotificationEnd);
+                previousNotificationStart = Math.max(drawStart, previousNotificationStart);
+            }
             float newYTranslation = state.yTranslation;
             float newHeight = state.height;
             // apply clipping and shadow
@@ -222,7 +186,7 @@
                 } else {
                     previousNotificationIsSwiped = ambientState.getDraggedViews().contains(child);
                     previousNotificationEnd = newNotificationEnd;
-                    previousNotificationStart = newYTranslation + state.clipTopAmount;
+                    previousNotificationStart =newYTranslation + state.clipTopAmount;
                 }
             }
         }
@@ -314,8 +278,20 @@
     /**
      * Initialize the algorithm state like updating the visible children.
      */
-    private void initAlgorithmState(StackScrollState resultState,
-            StackScrollAlgorithmState state) {
+    private void initAlgorithmState(StackScrollState resultState, StackScrollAlgorithmState state,
+            AmbientState ambientState) {
+        state.itemsInBottomStack = 0.0f;
+        state.partialInBottom = 0.0f;
+        float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
+
+        int scrollY = ambientState.getScrollY();
+
+        // Due to the overScroller, the stackscroller can have negative scroll state. This is
+        // already accounted for by the top padding and doesn't need an additional adaption
+        scrollY = Math.max(0, scrollY);
+        state.scrollY = (int) (scrollY + bottomOverScroll);
+
+        //now init the visible children and update paddings
         ViewGroup hostView = resultState.getHostView();
         int childCount = hostView.getChildCount();
         state.visibleChildren.clear();
@@ -383,15 +359,9 @@
         float bottomStackStart = bottomPeekStart - mBottomStackSlowDownLength;
 
         // The y coordinate of the current child.
-        float currentYPosition = 0.0f;
-
-        // How far in is the element currently transitioning into the bottom stack.
-        float yPositionInScrollView = 0.0f;
+        float currentYPosition = -algorithmState.scrollY;
 
         int childCount = algorithmState.visibleChildren.size();
-        int numberOfElementsCompletelyIn = algorithmState.partialInTop == 1.0f
-                ? algorithmState.lastTopStackIndex
-                : (int) algorithmState.itemsInTopStack;
         int paddingAfterChild;
         for (int i = 0; i < childCount; i++) {
             ExpandableView child = algorithmState.visibleChildren.get(i);
@@ -400,47 +370,16 @@
             paddingAfterChild = getPaddingAfterChild(algorithmState, child);
             int childHeight = getMaxAllowedChildHeight(child);
             int minHeight = child.getMinHeight();
-            float yPositionInScrollViewAfterElement = yPositionInScrollView
-                    + childHeight
-                    + paddingAfterChild;
-            float scrollOffset = yPositionInScrollView - algorithmState.scrollY +
-                    mFirstChildMinHeight;
-
-            if (i == algorithmState.lastTopStackIndex + 1) {
-                // Normally the position of this child is the position in the regular scrollview,
-                // but if the two stacks are very close to each other,
-                // then have have to push it even more upwards to the position of the bottom
-                // stack start.
-                currentYPosition = Math.min(scrollOffset, bottomStackStart);
-            }
             childViewState.yTranslation = currentYPosition;
+            if (i == 0) {
+                updateFirstChildHeight(child, childViewState, childHeight, ambientState);
+            }
 
             // The y position after this element
             float nextYPosition = currentYPosition + childHeight +
                     paddingAfterChild;
-
-            if (i <= algorithmState.lastTopStackIndex) {
+            if (nextYPosition >= bottomStackStart) {
                 // Case 1:
-                // We are in the top Stack
-                updateStateForTopStackChild(algorithmState, child,
-                        numberOfElementsCompletelyIn, i, childHeight, childViewState, scrollOffset);
-                clampPositionToTopStackEnd(childViewState, childHeight);
-
-                // check if we are overlapping with the bottom stack
-                if (childViewState.yTranslation + childHeight + paddingAfterChild
-                        >= bottomStackStart && !mIsExpansionChanging && i != 0) {
-                    // we just collapse this element slightly
-                    int newSize = (int) Math.max(bottomStackStart - paddingAfterChild -
-                            childViewState.yTranslation, minHeight);
-                    childViewState.height = newSize;
-                    updateStateForChildTransitioningInBottom(algorithmState, bottomStackStart,
-                            child, childViewState.yTranslation, childViewState,
-                            childHeight);
-                }
-                clampPositionToBottomStackStart(childViewState, childViewState.height,
-                        minHeight, ambientState);
-            } else if (nextYPosition >= bottomStackStart) {
-                // Case 2:
                 // We are in the bottom stack.
                 if (currentYPosition >= bottomStackStart) {
                     // According to the regular scroll view we are fully translated out of the
@@ -455,36 +394,30 @@
                             childViewState, childHeight);
                 }
             } else {
-                // Case 3:
+                // Case 2:
                 // We are in the regular scroll area.
                 childViewState.location = StackViewState.LOCATION_MAIN_AREA;
-                clampYTranslation(childViewState, childHeight, ambientState);
+                clampPositionToBottomStackStart(childViewState, childViewState.height, childHeight,
+                        ambientState);
             }
 
-            // The first card is always rendered.
-            if (i == 0) {
-                childViewState.hidden = false;
-                childViewState.shadowAlpha = 1.0f;
-                childViewState.yTranslation = Math.max(
-                        mFirstChildMinHeight - algorithmState.scrollY, 0);
-                if (childViewState.yTranslation + childViewState.height
-                        > bottomPeekStart - mCollapseSecondCardPadding) {
-                    childViewState.height = (int) Math.max(
-                            bottomPeekStart - mCollapseSecondCardPadding
-                                    - childViewState.yTranslation, mFirstChildMinHeight);
-                }
-                childViewState.location = StackViewState.LOCATION_FIRST_CARD;
+            if (i == 0 && ambientState.getScrollY() <= 0) {
+                // The first card can get into the bottom stack if it's the only one
+                // on the lockscreen which pushes it up. Let's make sure that doesn't happen and
+                // it stays at the top
+                childViewState.yTranslation = Math.max(0, childViewState.yTranslation);
+            }
+            currentYPosition = childViewState.yTranslation + childHeight + paddingAfterChild;
+            if (currentYPosition <= 0) {
+                childViewState.location = StackViewState.LOCATION_HIDDEN_TOP;
             }
             if (childViewState.location == StackViewState.LOCATION_UNKNOWN) {
                 Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
             }
-            currentYPosition = childViewState.yTranslation + childHeight + paddingAfterChild;
-            yPositionInScrollView = yPositionInScrollViewAfterElement;
 
             childViewState.yTranslation += ambientState.getTopPadding()
                     + ambientState.getStackTranslation();
         }
-        updateHeadsUpStates(resultState, algorithmState, ambientState);
     }
 
     private int getPaddingAfterChild(StackScrollAlgorithmState algorithmState,
@@ -506,24 +439,27 @@
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
             if (!row.isHeadsUp()) {
                 break;
-            } else if (topHeadsUpEntry == null) {
-                topHeadsUpEntry = row;
             }
             StackViewState childState = resultState.getViewStateForView(row);
+            if (topHeadsUpEntry == null) {
+                topHeadsUpEntry = row;
+                childState.location = StackViewState.LOCATION_FIRST_HUN;
+            }
             boolean isTopEntry = topHeadsUpEntry == row;
+            float unmodifiedEndLocation = childState.yTranslation + childState.height;
             if (mIsExpanded) {
-                // Ensure that the heads up is always visible even when scrolled off from the bottom
-                float bottomPosition = ambientState.getMaxHeadsUpTranslation() - childState.height;
-                childState.yTranslation = Math.min(childState.yTranslation,
-                        bottomPosition);
+                // Ensure that the heads up is always visible even when scrolled off
+                clampHunToTop(ambientState, row, childState);
+                clampHunToMaxTranslation(ambientState, row, childState);
             }
             if (row.isPinned()) {
                 childState.yTranslation = Math.max(childState.yTranslation, 0);
                 childState.height = Math.max(row.getIntrinsicHeight(), childState.height);
-                if (!isTopEntry) {
+                StackViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
+                if (!isTopEntry && (!mIsExpanded
+                        || unmodifiedEndLocation < topState.yTranslation + topState.height)) {
                     // Ensure that a headsUp doesn't vertically extend further than the heads-up at
                     // the top most z-position
-                    StackViewState topState = resultState.getViewStateForView(topHeadsUpEntry);
                     childState.height = row.getIntrinsicHeight();
                     childState.yTranslation = topState.yTranslation + topState.height
                             - childState.height;
@@ -532,17 +468,23 @@
         }
     }
 
-    /**
-     * Clamp the yTranslation both up and down to valid positions.
-     *
-     * @param childViewState the view state of the child
-     * @param minHeight the minimum height of this child
-     */
-    private void clampYTranslation(StackViewState childViewState, int minHeight,
-            AmbientState ambientState) {
-        clampPositionToBottomStackStart(childViewState, childViewState.height, minHeight,
-                ambientState);
-        clampPositionToTopStackEnd(childViewState, childViewState.height);
+    private void clampHunToTop(AmbientState ambientState, ExpandableNotificationRow row,
+            StackViewState childState) {
+        float newTranslation = Math.max(ambientState.getTopPadding()
+                + ambientState.getStackTranslation(), childState.yTranslation);
+        childState.height = (int) Math.max(childState.height - (newTranslation
+                - childState.yTranslation), row.getMinHeight());
+        childState.yTranslation = newTranslation;
+    }
+
+    private void clampHunToMaxTranslation(AmbientState ambientState, ExpandableNotificationRow row,
+            StackViewState childState) {
+        float newTranslation;
+        float bottomPosition = ambientState.getMaxHeadsUpTranslation() - row.getMinHeight();
+        newTranslation = Math.min(childState.yTranslation, bottomPosition);
+        childState.height = (int) Math.max(childState.height
+                - (childState.yTranslation - newTranslation), row.getMinHeight());
+        childState.yTranslation = newTranslation;
     }
 
     /**
@@ -569,19 +511,6 @@
         }
     }
 
-    /**
-     * Clamp the yTranslation of the child up such that its end is at lest on the end of the top
-     * stack.
-     *
-     * @param childViewState the view state of the child
-     * @param childHeight the height of this child
-     */
-    private void clampPositionToTopStackEnd(StackViewState childViewState,
-            int childHeight) {
-        childViewState.yTranslation = Math.max(childViewState.yTranslation,
-                mFirstChildMinHeight - childHeight);
-    }
-
     private int getMaxAllowedChildHeight(View child) {
         if (child instanceof ExpandableView) {
             ExpandableView expandableView = (ExpandableView) child;
@@ -611,9 +540,6 @@
         }
         childViewState.yTranslation = transitioningPositionStart + offset - newHeight
                 - getPaddingAfterChild(algorithmState, child);
-
-        // We want at least to be at the end of the top stack when collapsing
-        clampPositionToTopStackEnd(childViewState, newHeight);
         childViewState.location = StackViewState.LOCATION_MAIN_AREA;
     }
 
@@ -642,177 +568,59 @@
         }
         childViewState.height = minHeight;
         childViewState.yTranslation = currentYPosition - minHeight;
-        clampPositionToTopStackEnd(childViewState, minHeight);
     }
 
-    private void updateStateForTopStackChild(StackScrollAlgorithmState algorithmState,
-            ExpandableView child, int numberOfElementsCompletelyIn, int i, int childHeight,
-            StackViewState childViewState, float scrollOffset) {
-
-
-        // First we calculate the index relative to the current stack window of size at most
-        // {@link #MAX_ITEMS_IN_TOP_STACK}
-        int paddedIndex = i - 1
-                - Math.max(numberOfElementsCompletelyIn - MAX_ITEMS_IN_TOP_STACK, 0);
-        if (paddedIndex >= 0) {
-
-            // We are currently visually entering the top stack
-            float distanceToStack = (childHeight + getPaddingAfterChild(algorithmState, child))
-                    - algorithmState.scrolledPixelsTop;
-            if (i == algorithmState.lastTopStackIndex
-                    && distanceToStack > (mTopStackTotalSize
-                    + getPaddingAfterChild(algorithmState, child))) {
-
-                // Child is currently translating into stack but not yet inside slow down zone.
-                // Handle it like the regular scrollview.
-                childViewState.yTranslation = scrollOffset;
-            } else {
-                // Apply stacking logic.
-                float numItemsBefore;
-                if (i == algorithmState.lastTopStackIndex) {
-                    numItemsBefore = 1.0f
-                            - (distanceToStack / (mTopStackTotalSize
-                            + getPaddingAfterChild(algorithmState, child)));
-                } else {
-                    numItemsBefore = algorithmState.itemsInTopStack - i;
-                }
-                // The end position of the current child
-                float currentChildEndY = mFirstChildMinHeight + mTopStackTotalSize
-                        - mTopStackIndentationFunctor.getValue(numItemsBefore);
-                childViewState.yTranslation = currentChildEndY - childHeight;
-            }
-            childViewState.location = StackViewState.LOCATION_TOP_STACK_PEEKING;
-        } else {
-            if (paddedIndex == -1) {
-                childViewState.shadowAlpha = 1.0f - algorithmState.partialInTop;
-            } else {
-                // We are hidden behind the top card and faded out, so we can hide ourselves.
-                childViewState.hidden = true;
-                childViewState.shadowAlpha = 0.0f;
-            }
-            childViewState.yTranslation = mFirstChildMinHeight - childHeight;
-            childViewState.location = StackViewState.LOCATION_TOP_STACK_HIDDEN;
-        }
-
-
-    }
 
     /**
-     * Find the number of items in the top stack and update the result state if needed.
+     * Update the height of the first child i.e clamp it to the bottom stack
      *
-     * @param resultState The result state to update if a height change of an child occurs
-     * @param algorithmState The state in which the current pass of the algorithm is currently in
+     *
+
+     * @param child the child to update
+     * @param childViewState the viewstate of the child
+     * @param childHeight the height of the child
+     * @param ambientState The ambient state of the algorithm
      */
-    private void findNumberOfItemsInTopStackAndUpdateState(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
+    private void updateFirstChildHeight(ExpandableView child, StackViewState childViewState,
+            int childHeight, AmbientState ambientState) {
 
-        // The y Position if the element would be in a regular scrollView
-        float yPositionInScrollView = 0.0f;
-        int childCount = algorithmState.visibleChildren.size();
-        // find the number of elements in the top stack.
-        for (int i = 0; i < childCount; i++) {
-            ExpandableView child = algorithmState.visibleChildren.get(i);
-            StackViewState childViewState = resultState.getViewStateForView(child);
-            int childHeight = getMaxAllowedChildHeight(child);
-            int paddingAfterChild = getPaddingAfterChild(algorithmState, child);
-            float yPositionInScrollViewAfterElement = yPositionInScrollView
-                    + childHeight
-                    + paddingAfterChild;
-            if (yPositionInScrollView < algorithmState.scrollY) {
-                if (i == 0 && algorithmState.scrollY <= mFirstChildMinHeight) {
-
-                    // The starting position of the bottom stack peek
-                    int bottomPeekStart = ambientState.getInnerHeight() - mBottomStackPeekSize -
-                            mCollapseSecondCardPadding;
-                    // Collapse and expand the first child while the shade is being expanded
-                    float maxHeight = mIsExpansionChanging && child == mFirstChildWhileExpanding
-                            ? mFirstChildMaxHeight
-                            : childHeight;
-                    childViewState.height = (int) Math.max(Math.min(bottomPeekStart, maxHeight),
-                            mFirstChildMinHeight);
-                    algorithmState.itemsInTopStack = 1.0f;
-
-                } else if (yPositionInScrollViewAfterElement < algorithmState.scrollY) {
-                    // According to the regular scroll view we are fully off screen
-                    algorithmState.itemsInTopStack += 1.0f;
-                    if (i == 0) {
-                        childViewState.height = child.getMinHeight();
-                    }
-                } else {
-                    // According to the regular scroll view we are partially off screen
-
-                    // How much did we scroll into this child
-                    algorithmState.scrolledPixelsTop = algorithmState.scrollY
-                            - yPositionInScrollView;
-                    algorithmState.partialInTop = (algorithmState.scrolledPixelsTop) / (childHeight
-                            + paddingAfterChild);
-
-                    // Our element can be expanded, so this can get negative
-                    algorithmState.partialInTop = Math.max(0.0f, algorithmState.partialInTop);
-                    algorithmState.itemsInTopStack += algorithmState.partialInTop;
-
-                    if (i == 0) {
-                        // If it is expanded we have to collapse it to a new size
-                        float newSize = yPositionInScrollViewAfterElement
-                                - paddingAfterChild
-                                - algorithmState.scrollY + mFirstChildMinHeight;
-                        newSize = Math.max(mFirstChildMinHeight, newSize);
-                        algorithmState.itemsInTopStack = 1.0f;
-                        childViewState.height = (int) newSize;
-                    }
-                    algorithmState.lastTopStackIndex = i;
-                    break;
-                }
-            } else {
-                algorithmState.lastTopStackIndex = i - 1;
-                // We are already past the stack so we can end the loop
-                break;
-            }
-            yPositionInScrollView = yPositionInScrollViewAfterElement;
-        }
+            // The starting position of the bottom stack peek
+            int bottomPeekStart = ambientState.getInnerHeight() - mBottomStackPeekSize -
+                    mCollapseSecondCardPadding + ambientState.getScrollY();
+            // Collapse and expand the first child while the shade is being expanded
+            float maxHeight = mIsExpansionChanging && child == mFirstChildWhileExpanding
+                    ? mFirstChildMaxHeight
+                    : childHeight;
+            childViewState.height = (int) Math.max(Math.min(bottomPeekStart, maxHeight),
+                    child.getMinHeight());
     }
 
     /**
      * Calculate the Z positions for all children based on the number of items in both stacks and
      * save it in the resultState
-     *
-     * @param resultState The result state to update the zTranslation values
+     *  @param resultState The result state to update the zTranslation values
      * @param algorithmState The state in which the current pass of the algorithm is currently in
+     * @param ambientState The ambient state of the algorithm
      */
     private void updateZValuesForState(StackScrollState resultState,
-            StackScrollAlgorithmState algorithmState) {
+            StackScrollAlgorithmState algorithmState, AmbientState ambientState) {
         int childCount = algorithmState.visibleChildren.size();
-        for (int i = 0; i < childCount; i++) {
-            View child = algorithmState.visibleChildren.get(i);
+        int childrenOnTop = 0;
+        for (int i = childCount - 1; i >= 0; i--) {
+            ExpandableView child = algorithmState.visibleChildren.get(i);
             StackViewState childViewState = resultState.getViewStateForView(child);
-            if (i < algorithmState.itemsInTopStack) {
-                float stackIndex = algorithmState.itemsInTopStack - i;
-
-                // Ensure that the topmost item is a little bit higher than the rest when fully
-                // scrolled, to avoid drawing errors when swiping it out
-                float max = MAX_ITEMS_IN_TOP_STACK + (i == 0 ? 2.5f : 2);
-                stackIndex = Math.min(stackIndex, max);
-                if (i == 0 && algorithmState.itemsInTopStack < 2.0f) {
-
-                    // We only have the top item and an additional item in the top stack,
-                    // Interpolate the index from 0 to 2 while the second item is
-                    // translating in.
-                    stackIndex -= 1.0f;
-                    if (algorithmState.scrollY > mFirstChildMinHeight) {
-
-                        // Since there is a shadow treshhold, we cant just interpolate from 0 to
-                        // 2 but we interpolate from 0.1f to 2.0f when scrolled in. The jump in
-                        // height will not be noticable since we have padding in between.
-                        stackIndex = 0.1f + stackIndex * 1.9f;
-                    }
-                }
-                childViewState.zTranslation = mZBasicHeight
-                        + stackIndex * mZDistanceBetweenElements;
-            } else if (i > (childCount - 1 - algorithmState.itemsInBottomStack)) {
+            if (i > (childCount - 1 - algorithmState.itemsInBottomStack)) {
+                // We are in the bottom stack
                 float numItemsAbove = i - (childCount - 1 - algorithmState.itemsInBottomStack);
-                float translationZ = mZBasicHeight
+                childViewState.zTranslation = mZBasicHeight
                         - numItemsAbove * mZDistanceBetweenElements;
-                childViewState.zTranslation = translationZ;
+            } else if (child.mustStayOnScreen()
+                    && childViewState.yTranslation < ambientState.getTopPadding()
+                    + ambientState.getStackTranslation()) {
+                // TODO; do this more cleanly
+                childrenOnTop++;
+                childViewState.zTranslation = mZBasicHeight
+                        + childrenOnTop * mZDistanceBetweenElements;
             } else {
                 childViewState.zTranslation = mZBasicHeight;
             }
@@ -897,7 +705,6 @@
     }
 
     public void notifyChildrenChanged(final NotificationStackScrollLayout hostView) {
-        mFirstChild = hostView.getFirstChildNotGone();
         if (mIsExpansionChanging) {
             hostView.post(new Runnable() {
                 @Override
@@ -922,26 +729,6 @@
         public int scrollY;
 
         /**
-         *  The quantity of items which are in the top stack.
-         */
-        public float itemsInTopStack;
-
-        /**
-         * how far in is the element currently transitioning into the top stack
-         */
-        public float partialInTop;
-
-        /**
-         * The number of pixels the last child in the top stack has scrolled in to the stack
-         */
-        public float scrolledPixelsTop;
-
-        /**
-         * The last item index which is in the top stack.
-         */
-        public int lastTopStackIndex;
-
-        /**
          * The quantity of items which are in the bottom stack.
          */
         public float itemsInBottomStack;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
index 05fa27d..fa15195 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackViewState.java
@@ -24,12 +24,11 @@
     // These are flags such that we can create masks for filtering.
 
     public static final int LOCATION_UNKNOWN = 0x00;
-    public static final int LOCATION_FIRST_CARD = 0x01;
-    public static final int LOCATION_TOP_STACK_HIDDEN = 0x02;
-    public static final int LOCATION_TOP_STACK_PEEKING = 0x04;
-    public static final int LOCATION_MAIN_AREA = 0x08;
-    public static final int LOCATION_BOTTOM_STACK_PEEKING = 0x10;
-    public static final int LOCATION_BOTTOM_STACK_HIDDEN = 0x20;
+    public static final int LOCATION_FIRST_HUN = 0x01;
+    public static final int LOCATION_HIDDEN_TOP = 0x02;
+    public static final int LOCATION_MAIN_AREA = 0x04;
+    public static final int LOCATION_BOTTOM_STACK_PEEKING = 0x08;
+    public static final int LOCATION_BOTTOM_STACK_HIDDEN = 0x10;
     /** The view isn't layouted at all. */
     public static final int LOCATION_GONE = 0x40;
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c8645b4..819250cf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8220,7 +8220,7 @@
      *
      * If {@code extractLibs} is true, native libraries are extracted from the app if required.
      */
-    public void derivePackageAbi(PackageParser.Package pkg, File scanFile,
+    private void derivePackageAbi(PackageParser.Package pkg, File scanFile,
                                  String cpuAbiOverride, boolean extractLibs)
             throws PackageManagerException {
         // TODO: We can probably be smarter about this stuff. For installed apps,
@@ -8301,16 +8301,17 @@
                 if (abi32 >= 0) {
                     final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
                     if (abi64 >= 0) {
-                        pkg.applicationInfo.secondaryCpuAbi = abi;
+                        if (cpuAbiOverride == null && pkg.use32bitAbi) {
+                            pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
+                            pkg.applicationInfo.primaryCpuAbi = abi;
+                        } else {
+                            pkg.applicationInfo.secondaryCpuAbi = abi;
+                        }
                     } else {
                         pkg.applicationInfo.primaryCpuAbi = abi;
                     }
                 }
-                if (cpuAbiOverride != null &&
-                        cpuAbiOverride.equals(pkg.applicationInfo.secondaryCpuAbi)) {
-                    pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
-                    pkg.applicationInfo.primaryCpuAbi = cpuAbiOverride;
-                }
+
             } else {
                 String[] abiList = (cpuAbiOverride != null) ?
                         new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index c122c5a..f1cbb9a 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -774,9 +774,9 @@
     }
 
     /**
-     * Register a {@link PhoneAccount} for use by the system. When registering
-     * {@link PhoneAccount}s, existing registrations will be overwritten if the
-     * {@link PhoneAccountHandle} matches that of a {@link PhoneAccount} which is already
+     * Register a {@link PhoneAccount} for use by the system that will be stored in Device Encrypted
+     * storage. When registering {@link PhoneAccount}s, existing registrations will be overwritten
+     * if the {@link PhoneAccountHandle} matches that of a {@link PhoneAccount} which is already
      * registered. Once registered, the {@link PhoneAccount} is listed to the user as an option
      * when placing calls. The user may still need to enable the {@link PhoneAccount} within
      * the phone app settings before the account is usable.
@@ -1166,11 +1166,16 @@
     /**
      * Registers a new incoming call. A {@link ConnectionService} should invoke this method when it
      * has an incoming call. The specified {@link PhoneAccountHandle} must have been registered
-     * with {@link #registerPhoneAccount}. Once invoked, this method will cause the system to bind
-     * to the {@link ConnectionService} associated with the {@link PhoneAccountHandle} and request
-     * additional information about the call (See
-     * {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming call UI.
-     *
+     * with {@link #registerPhoneAccount} and the user must have enabled the corresponding
+     * {@link PhoneAccount}. This can be checked using {@link #getPhoneAccount}. Once invoked, this
+     * method will cause the system to bind to the {@link ConnectionService} associated with the
+     * {@link PhoneAccountHandle} and request additional information about the call
+     * (See {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming
+     * call UI.
+     * <p>
+     * A {@link SecurityException} will be thrown if either the {@link PhoneAccountHandle} does not
+     * correspond to a registered {@link PhoneAccount} or the associated {@link PhoneAccount} is not
+     * currently enabled by the user.
      * @param phoneAccount A {@link PhoneAccountHandle} registered with
      *            {@link #registerPhoneAccount}.
      * @param extras A bundle that will be passed through to
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 4a86c59..31da670 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -332,6 +332,7 @@
     public static class InformationElement {
         public static final int EID_SSID = 0;
         public static final int EID_BSS_LOAD = 11;
+        public static final int EID_RSN = 48;
         public static final int EID_HT_OPERATION = 61;
         public static final int EID_INTERWORKING = 107;
         public static final int EID_ROAMING_CONSORTIUM = 111;
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index a0dbd85..362738e 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -101,6 +101,8 @@
     /** @hide */
     public static final String CA_CERT_KEY         = "ca_cert";
     /** @hide */
+    public static final String CA_PATH_KEY         = "ca_path";
+    /** @hide */
     public static final String ENGINE_KEY          = "engine";
     /** @hide */
     public static final String ENGINE_ID_KEY       = "engine_id";
@@ -625,6 +627,33 @@
         mCaCerts = null;
     }
 
+    /**
+     * Set the ca_path directive on wpa_supplicant.
+     *
+     * From wpa_supplicant documentation:
+     *
+     * Directory path for CA certificate files (PEM). This path may contain
+     * multiple CA certificates in OpenSSL format. Common use for this is to
+     * point to system trusted CA list which is often installed into directory
+     * like /etc/ssl/certs. If configured, these certificates are added to the
+     * list of trusted CAs. ca_cert may also be included in that case, but it is
+     * not required.
+     * @param domain The path for CA certificate files
+     * @hide
+     */
+    public void setCaPath(String path) {
+        setFieldValue(CA_PATH_KEY, path);
+    }
+
+    /**
+     * Get the domain_suffix_match value. See setDomSuffixMatch.
+     * @return The path for CA certificate files.
+     * @hide
+     */
+    public String getCaPath() {
+        return getFieldValue(CA_PATH_KEY, "");
+    }
+
     /** Set Client certificate alias.
      *
      * <p> See the {@link android.security.KeyChain} for details on installing or choosing