Merge "Exposing setRecentsVisibility method and associated callbacks to the Recents components" into pi-dev
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
index 28d4096..ba072da 100644
--- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -69,7 +69,6 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         Binder b = new Binder();
         mBinderCallsStats = new BinderCallsStats(false);
-        assertNull(mBinderCallsStats.callStarted(b, 0));
         while (state.keepRunning()) {
             BinderCallsStats.CallSession s = mBinderCallsStats.callStarted(b, 0);
             mBinderCallsStats.callEnded(s);
diff --git a/core/java/android/text/AndroidBidi.java b/core/java/android/text/AndroidBidi.java
index 179d545..72383cf 100644
--- a/core/java/android/text/AndroidBidi.java
+++ b/core/java/android/text/AndroidBidi.java
@@ -32,8 +32,12 @@
 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
 public class AndroidBidi {
 
-    private static class EmojiBidiOverride extends BidiClassifier {
-        EmojiBidiOverride() {
+    /**
+     * Overrides ICU {@link BidiClassifier} in order to correctly handle character directions for
+     * newest emoji that ICU is not aware of.
+     */
+    public static class EmojiBidiOverride extends BidiClassifier {
+        public EmojiBidiOverride() {
             super(null /* No persisting object needed */);
         }
 
diff --git a/core/java/android/text/BidiFormatter.java b/core/java/android/text/BidiFormatter.java
index f65f397..77f17a7 100644
--- a/core/java/android/text/BidiFormatter.java
+++ b/core/java/android/text/BidiFormatter.java
@@ -21,6 +21,8 @@
 import android.annotation.Nullable;
 import android.view.View;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.Locale;
 
 /**
@@ -570,8 +572,10 @@
     /**
      * An object that estimates the directionality of a given string by various methods.
      *
+     * @hide
      */
-    private static class DirectionalityEstimator {
+    @VisibleForTesting
+    public static class DirectionalityEstimator {
 
         // Internal static variables and constants.
 
@@ -598,7 +602,11 @@
             }
         }
 
-        private static byte getDirectionality(int codePoint) {
+        /**
+         * Return Character directionality. Same as {@link Character#getDirectionality(int)} except
+         * it overrides values for newest emoji that are not covered by ICU.
+         */
+        public static byte getDirectionality(int codePoint) {
             if (Emoji.isNewEmoji(codePoint)) {
                 // TODO: Fix or remove once emoji-data.text 5.0 is in ICU or update to 6.0.
                 return Character.DIRECTIONALITY_OTHER_NEUTRALS;
diff --git a/core/java/android/text/Emoji.java b/core/java/android/text/Emoji.java
index d33aad9..876c64e 100644
--- a/core/java/android/text/Emoji.java
+++ b/core/java/android/text/Emoji.java
@@ -46,44 +46,49 @@
         return UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI_MODIFIER);
     }
 
-    // Returns true if the given code point is emoji modifier base.
-    public static boolean isEmojiModifierBase(int codePoint) {
+    //
+
+    /**
+     * Returns true if the given code point is emoji modifier base.
+     * @param c codepoint to check
+     * @return true if is emoji modifier base
+     */
+    public static boolean isEmojiModifierBase(int c) {
         // These two characters were removed from Emoji_Modifier_Base in Emoji 4.0, but we need to
         // keep them as emoji modifier bases since there are fonts and user-generated text out there
         // that treats these as potential emoji bases.
-        if (codePoint == 0x1F91D || codePoint == 0x1F93C) {
+        if (c == 0x1F91D || c == 0x1F93C) {
             return true;
         }
-        // Emoji Modifier Base characters new in Unicode emoji 5.0.
-        // From http://www.unicode.org/Public/emoji/5.0/emoji-data.txt
-        // TODO: Remove once emoji-data.text 5.0 is in ICU or update to 6.0.
-        if (codePoint == 0x1F91F
-                || (0x1F931 <= codePoint && codePoint <= 0x1F932)
-                || (0x1F9D1 <= codePoint && codePoint <= 0x1F9DD)) {
+        // Emoji Modifier Base characters new in Unicode emoji 11
+        // From https://www.unicode.org/Public/emoji/11.0/emoji-data.txt
+        // TODO: Remove once emoji-data.text 11 is in ICU or update to 11.
+        if ((0x1F9B5 <= c && c <= 0x1F9B6) || (0x1F9B8 <= c && c <= 0x1F9B9)) {
             return true;
         }
-        return UCharacter.hasBinaryProperty(codePoint, UProperty.EMOJI_MODIFIER_BASE);
+        return UCharacter.hasBinaryProperty(c, UProperty.EMOJI_MODIFIER_BASE);
     }
 
     /**
      * Returns true if the character is a new emoji still not supported in our version of ICU.
      */
-    public static boolean isNewEmoji(int codePoint) {
-        // Emoji characters new in Unicode emoji 5.0.
-        // From http://www.unicode.org/Public/emoji/5.0/emoji-data.txt
-        // TODO: Remove once emoji-data.text 5.0 is in ICU or update to 6.0.
-        if (codePoint < 0x1F6F7 || codePoint > 0x1F9E6) {
+    public static boolean isNewEmoji(int c) {
+        // Emoji characters new in Unicode emoji 11
+        // From https://www.unicode.org/Public/emoji/11.0/emoji-data.txt
+        // TODO: Remove once emoji-data.text 11 is in ICU or update to 11.
+        if (c < 0x1F6F9 || c > 0x1F9FF) {
             // Optimization for characters outside the new emoji range.
             return false;
         }
-        return (0x1F6F7 <= codePoint && codePoint <= 0x1F6F8)
-                || codePoint == 0x1F91F
-                || (0x1F928 <= codePoint && codePoint <= 0x1F92F)
-                || (0x1F931 <= codePoint && codePoint <= 0x1F932)
-                || codePoint == 0x1F94C
-                || (0x1F95F <= codePoint && codePoint <= 0x1F96B)
-                || (0x1F992 <= codePoint && codePoint <= 0x1F997)
-                || (0x1F9D0 <= codePoint && codePoint <= 0x1F9E6);
+        return c == 0x265F || c == 0x267E || c == 0x1F6F9 || c == 0x1F97A
+                || (0x1F94D <= c && c <= 0x1F94F)
+                || (0x1F96C <= c && c <= 0x1F970)
+                || (0x1F973 <= c && c <= 0x1F976)
+                || (0x1F97C <= c && c <= 0x1F97F)
+                || (0x1F998 <= c && c <= 0x1F9A2)
+                || (0x1F9B0 <= c && c <= 0x1F9B9)
+                || (0x1F9C1 <= c && c <= 0x1F9C2)
+                || (0x1F9E7 <= c && c <= 0x1F9FF);
     }
 
     /**
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 2c48506..7eefe59 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -21,6 +21,7 @@
 import android.util.ArrayMap;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
@@ -40,23 +41,21 @@
     private static final int CALL_SESSIONS_POOL_SIZE = 100;
     private static final BinderCallsStats sInstance = new BinderCallsStats();
 
-    private volatile boolean mTrackingEnabled = false;
+    private volatile boolean mDetailedTracking = false;
+    @GuardedBy("mLock")
     private final SparseArray<UidEntry> mUidEntries = new SparseArray<>();
     private final Queue<CallSession> mCallSessionsPool = new ConcurrentLinkedQueue<>();
+    private final Object mLock = new Object();
 
     private BinderCallsStats() {
     }
 
     @VisibleForTesting
-    public BinderCallsStats(boolean trackingEnabled) {
-        mTrackingEnabled = trackingEnabled;
+    public BinderCallsStats(boolean detailedTracking) {
+        mDetailedTracking = detailedTracking;
     }
 
     public CallSession callStarted(Binder binder, int code) {
-        if (!mTrackingEnabled) {
-            return null;
-        }
-
         return callStarted(binder.getClass().getName(), code);
     }
 
@@ -73,32 +72,31 @@
     }
 
     public void callEnded(CallSession s) {
-        if (!mTrackingEnabled) {
-            return;
-        }
         Preconditions.checkNotNull(s);
-        final long cpuTimeNow = getThreadTimeMicro();
-        final long duration = cpuTimeNow - s.mStarted;
+        long duration = mDetailedTracking ? getThreadTimeMicro() - s.mStarted : 1;
         s.mCallingUId = Binder.getCallingUid();
 
-        synchronized (mUidEntries) {
+        synchronized (mLock) {
             UidEntry uidEntry = mUidEntries.get(s.mCallingUId);
             if (uidEntry == null) {
                 uidEntry = new UidEntry(s.mCallingUId);
                 mUidEntries.put(s.mCallingUId, uidEntry);
             }
 
-            // Find CallDesc entry and update its total time
-            CallStat callStat = uidEntry.mCallStats.get(s.mCallStat);
-            // Only create CallStat if it's a new entry, otherwise update existing instance
-            if (callStat == null) {
-                callStat = new CallStat(s.mCallStat.className, s.mCallStat.msg);
-                uidEntry.mCallStats.put(callStat, callStat);
+            if (mDetailedTracking) {
+                // Find CallDesc entry and update its total time
+                CallStat callStat = uidEntry.mCallStats.get(s.mCallStat);
+                // Only create CallStat if it's a new entry, otherwise update existing instance
+                if (callStat == null) {
+                    callStat = new CallStat(s.mCallStat.className, s.mCallStat.msg);
+                    uidEntry.mCallStats.put(callStat, callStat);
+                }
+                callStat.callCount++;
+                callStat.time += duration;
             }
+
             uidEntry.time += duration;
             uidEntry.callCount++;
-            callStat.callCount++;
-            callStat.time += duration;
         }
         if (mCallSessionsPool.size() < CALL_SESSIONS_POOL_SIZE) {
             mCallSessionsPool.add(s);
@@ -112,7 +110,7 @@
         long totalCallsTime = 0;
         int uidEntriesSize = mUidEntries.size();
         List<UidEntry> entries = new ArrayList<>();
-        synchronized (mUidEntries) {
+        synchronized (mLock) {
             for (int i = 0; i < uidEntriesSize; i++) {
                 UidEntry e = mUidEntries.valueAt(i);
                 entries.add(e);
@@ -127,20 +125,10 @@
                 totalCallsCount += e.callCount;
             }
         }
-        pw.println("Binder call stats:");
-        pw.println("  Raw data (uid,call_desc,time):");
-        entries.sort((o1, o2) -> {
-            if (o1.time < o2.time) {
-                return 1;
-            } else if (o1.time > o2.time) {
-                return -1;
-            }
-            return 0;
-        });
-        StringBuilder sb = new StringBuilder();
-        for (UidEntry uidEntry : entries) {
-            List<CallStat> callStats = new ArrayList<>(uidEntry.mCallStats.keySet());
-            callStats.sort((o1, o2) -> {
+        if (mDetailedTracking) {
+            pw.println("Binder call stats:");
+            pw.println("  Raw data (uid,call_desc,time):");
+            entries.sort((o1, o2) -> {
                 if (o1.time < o2.time) {
                     return 1;
                 } else if (o1.time > o2.time) {
@@ -148,44 +136,72 @@
                 }
                 return 0;
             });
-            for (CallStat e : callStats) {
-                sb.setLength(0);
-                sb.append("    ")
-                        .append(uidEntry.uid).append(",").append(e).append(',').append(e.time);
-                pw.println(sb);
+            StringBuilder sb = new StringBuilder();
+            for (UidEntry uidEntry : entries) {
+                List<CallStat> callStats = new ArrayList<>(uidEntry.mCallStats.keySet());
+                callStats.sort((o1, o2) -> {
+                    if (o1.time < o2.time) {
+                        return 1;
+                    } else if (o1.time > o2.time) {
+                        return -1;
+                    }
+                    return 0;
+                });
+                for (CallStat e : callStats) {
+                    sb.setLength(0);
+                    sb.append("    ")
+                            .append(uidEntry.uid).append(",").append(e).append(',').append(e.time);
+                    pw.println(sb);
+                }
+            }
+            pw.println();
+            pw.println("  Per UID Summary(UID: time, % of total_time, calls_count):");
+            List<Map.Entry<Integer, Long>> uidTotals = new ArrayList<>(uidTimeMap.entrySet());
+            uidTotals.sort((o1, o2) -> o2.getValue().compareTo(o1.getValue()));
+            for (Map.Entry<Integer, Long> uidTotal : uidTotals) {
+                Long callCount = uidCallCountMap.get(uidTotal.getKey());
+                pw.println(String.format("    %7d: %11d %3.0f%% %8d",
+                        uidTotal.getKey(), uidTotal.getValue(),
+                        100d * uidTotal.getValue() / totalCallsTime, callCount));
+            }
+            pw.println();
+            pw.println(String.format("  Summary: total_time=%d, "
+                            + "calls_count=%d, avg_call_time=%.0f",
+                    totalCallsTime, totalCallsCount,
+                    (double)totalCallsTime / totalCallsCount));
+        } else {
+            pw.println("  Per UID Summary(UID: calls_count, % of total calls_count):");
+            List<Map.Entry<Integer, Long>> uidTotals = new ArrayList<>(uidTimeMap.entrySet());
+            uidTotals.sort((o1, o2) -> o2.getValue().compareTo(o1.getValue()));
+            for (Map.Entry<Integer, Long> uidTotal : uidTotals) {
+                Long callCount = uidCallCountMap.get(uidTotal.getKey());
+                pw.println(String.format("    %7d: %8d %3.0f%%",
+                        uidTotal.getKey(), callCount, 100d * uidTotal.getValue() / totalCallsTime));
             }
         }
-        pw.println();
-        pw.println("  Per UID Summary(UID: time, total_time_percentage, calls_count):");
-        List<Map.Entry<Integer, Long>> uidTotals = new ArrayList<>(uidTimeMap.entrySet());
-        uidTotals.sort((o1, o2) -> o2.getValue().compareTo(o1.getValue()));
-        for (Map.Entry<Integer, Long> uidTotal : uidTotals) {
-            Long callCount = uidCallCountMap.get(uidTotal.getKey());
-            pw.println(String.format("    %5d: %11d %3.0f%% %8d",
-                    uidTotal.getKey(), uidTotal.getValue(),
-                    100d * uidTotal.getValue() / totalCallsTime, callCount));
-        }
-        pw.println();
-        pw.println(String.format("  Summary: total_time=%d, "
-                        + "calls_count=%d, avg_call_time=%.0f",
-                totalCallsTime, totalCallsCount,
-                (double)totalCallsTime / totalCallsCount));
     }
 
-    private static long getThreadTimeMicro() {
-        return SystemClock.currentThreadTimeMicro();
+    private long getThreadTimeMicro() {
+        // currentThreadTimeMicro is expensive, so we measure cpu time only if detailed tracking is
+        // enabled
+        return mDetailedTracking ? SystemClock.currentThreadTimeMicro() : 0;
     }
 
     public static BinderCallsStats getInstance() {
         return sInstance;
     }
 
-    public void setTrackingEnabled(boolean enabled) {
-        mTrackingEnabled = enabled;
+    public void setDetailedTracking(boolean enabled) {
+        if (enabled != mDetailedTracking) {
+            reset();
+            mDetailedTracking = enabled;
+        }
     }
 
-    public boolean isTrackingEnabled() {
-        return mTrackingEnabled;
+    public void reset() {
+        synchronized (mLock) {
+            mUidEntries.clear();
+        }
     }
 
     private static class CallStat {
@@ -210,7 +226,7 @@
 
             CallStat callStat = (CallStat) o;
 
-            return msg == callStat.msg && (className == callStat.className);
+            return msg == callStat.msg && (className.equals(callStat.className));
         }
 
         @Override
diff --git a/core/res/res/layout/global_actions_item.xml b/core/res/res/layout/global_actions_item.xml
index e5a9854..6ac0b17 100644
--- a/core/res/res/layout/global_actions_item.xml
+++ b/core/res/res/layout/global_actions_item.xml
@@ -13,6 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<!-- Used with LegacyGlobalActions. -->
 
 <!-- RelativeLayouts have an issue enforcing minimum heights, so just
      work around this for now with LinearLayouts. -->
diff --git a/core/tests/coretests/src/android/text/EmojiTest.java b/core/tests/coretests/src/android/text/EmojiTest.java
new file mode 100644
index 0000000..313f1b6
--- /dev/null
+++ b/core/tests/coretests/src/android/text/EmojiTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2018 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.text;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.icu.lang.UCharacterDirection;
+import android.icu.text.Bidi;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Emoji and ICU drops does not happen at the same time. Therefore there are almost always cases
+ * where the existing ICU version is not aware of the latest emoji that Android supports.
+ * This test covers Emoji and ICU related functions where other components such as
+ * {@link AndroidBidi}, {@link BidiFormatter} depend on. The tests are collected into the same
+ * class since the changes effect all those classes.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EmojiTest {
+
+    @Test
+    public void testIsNewEmoji_Emoji5() {
+        // each row in the data is the range of emoji
+        final int[][][] data = new int[][][]{
+                {       // EMOJI 5
+                        // range of emoji: i.e from 0x1F6F7 to 0x1F6F8 inclusive
+                        {0x1F6F7, 0x1F6F8},
+                        {0x1F91F, 0x1F91F},
+                        {0x1F928, 0x1F92F},
+                        {0x1F94C, 0x1F94C},
+                        {0x1F95F, 0x1F96B},
+                        {0x1F992, 0x1F997},
+                        {0x1F9D0, 0x1F9E6},
+                },
+                {       // EMOJI 11
+                        {0x265F, 0x265F},
+                        {0x267E, 0x267E},
+                        {0x1F6F9, 0x1F6F9},
+                        {0x1F94D, 0x1F94F},
+                        {0x1F96C, 0x1F970},
+                        {0x1F973, 0x1F976},
+                        {0x1F97A, 0x1F97A},
+                        {0x1F97C, 0x1F97F},
+                        {0x1F998, 0x1F9A2},
+                        {0x1F9B0, 0x1F9B9},
+                        {0x1F9C1, 0x1F9C2},
+                        {0x1F9E7, 0x1F9FF},
+                }
+        };
+
+        final Bidi icuBidi = new Bidi(0 /* maxLength */, 0 /* maxRunCount */);
+        icuBidi.setCustomClassifier(new AndroidBidi.EmojiBidiOverride());
+
+        for (int version = 0; version < data.length; version++) {
+            for (int row = 0; row < data[version].length; row++) {
+                for (int c = data[version][row][0]; c < data[version][row][1]; c++) {
+                    assertTrue(Integer.toHexString(c) + " should be emoji", Emoji.isEmoji(c));
+
+                    assertEquals(Integer.toHexString(c) + " should have neutral directionality",
+                            Character.DIRECTIONALITY_OTHER_NEUTRALS,
+                            BidiFormatter.DirectionalityEstimator.getDirectionality(c));
+
+                    assertEquals(Integer.toHexString(c) + " shoud be OTHER_NEUTRAL for ICU Bidi",
+                            UCharacterDirection.OTHER_NEUTRAL, icuBidi.getCustomizedClass(c));
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testisEmojiModifierBase_LegacyCompat() {
+        assertTrue(Emoji.isEmojiModifierBase(0x1F91D));
+        assertTrue(Emoji.isEmojiModifierBase(0x1F93C));
+    }
+
+    @Test
+    public void testisEmojiModifierBase() {
+        // each row in the data is the range of emoji
+        final int[][][] data = new int[][][]{
+                {       // EMOJI 5
+                        // range of emoji: i.e from 0x1F91F to 0x1F91F inclusive
+                        {0x1F91F, 0x1F91F},
+                        {0x1F931, 0x1F932},
+                        {0x1F9D1, 0x1F9DD},
+                },
+                {       // EMOJI 11
+                        {0x1F9B5, 0x1F9B6},
+                        {0x1F9B8, 0x1F9B9}
+                }
+        };
+        for (int version = 0; version < data.length; version++) {
+            for (int row = 0; row < data[version].length; row++) {
+                for (int c = data[version][row][0]; c < data[version][row][1]; c++) {
+                    assertTrue(Integer.toHexString(c) + " should be emoji modifier base",
+                            Emoji.isEmojiModifierBase(c));
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/res/layout/global_actions_item.xml b/packages/SystemUI/res/layout/global_actions_item.xml
index bf3aea8..66a4b73 100644
--- a/packages/SystemUI/res/layout/global_actions_item.xml
+++ b/packages/SystemUI/res/layout/global_actions_item.xml
@@ -25,8 +25,8 @@
     android:minHeight="92dp"
     android:gravity="center"
     android:orientation="vertical"
-    android:paddingEnd="0dip"
-    android:paddingStart="0dip">
+    android:paddingEnd="4dip"
+    android:paddingStart="4dip">
 
     <ImageView
         android:id="@*android:id/icon"
diff --git a/packages/SystemUI/res/layout/global_actions_wrapped.xml b/packages/SystemUI/res/layout/global_actions_wrapped.xml
index 4a6af9e..b715def 100644
--- a/packages/SystemUI/res/layout/global_actions_wrapped.xml
+++ b/packages/SystemUI/res/layout/global_actions_wrapped.xml
@@ -17,7 +17,7 @@
         android:layout_gravity="top|right"
         android:gravity="center"
         android:orientation="vertical"
-        android:padding="16dp"
+        android:padding="12dp"
         android:translationZ="9dp" />
 
 </com.android.systemui.HardwareUiLayout>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a1e5b0e..875000c 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -899,8 +899,6 @@
     <dimen name="default_gear_space">18dp</dimen>
     <dimen name="cell_overlay_padding">18dp</dimen>
 
-    <dimen name="hwui_edge_margin">16dp</dimen>
-
     <dimen name="global_actions_panel_width">120dp</dimen>
 
     <dimen name="global_actions_top_padding">120dp</dimen>
@@ -916,7 +914,7 @@
     <dimen name="corner_size">8dp</dimen>
     <dimen name="top_padding">0dp</dimen>
     <dimen name="bottom_padding">48dp</dimen>
-    <dimen name="edge_margin">16dp</dimen>
+    <dimen name="edge_margin">8dp</dimen>
 
     <dimen name="rounded_corner_radius">0dp</dimen>
     <dimen name="rounded_corner_content_padding">0dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
index 3bdb247..98dc321 100644
--- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
@@ -89,7 +89,7 @@
         mEdgeBleed = Settings.Secure.getInt(getContext().getContentResolver(),
                 EDGE_BLEED, 0) != 0;
         mRoundedDivider = Settings.Secure.getInt(getContext().getContentResolver(),
-                ROUNDED_DIVIDER, 1) != 0;
+                ROUNDED_DIVIDER, 0) != 0;
         updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding());
         mBackground = new HardwareBgDrawable(mRoundedDivider, !mEdgeBleed, getContext());
         if (mChild != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
index 8515bf2..4fea45c3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/AlwaysOnDisplayPolicy.java
@@ -116,6 +116,7 @@
     private SettingsObserver mSettingsObserver;
 
     public AlwaysOnDisplayPolicy(Context context) {
+        context = context.getApplicationContext();
         mContext = context;
         mParser = new KeyValueListParser(',');
         mSettingsObserver = new SettingsObserver(context.getMainThreadHandler());
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 5bf62f6..e9d78d9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -61,13 +61,14 @@
 
         DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock);
         machine.setParts(new DozeMachine.Part[]{
-                new DozePauser(handler, machine, alarmManager, new AlwaysOnDisplayPolicy(context)),
+                new DozePauser(handler, machine, alarmManager, params.getPolicy()),
                 new DozeFalsingManagerAdapter(FalsingManager.getInstance(context)),
                 createDozeTriggers(context, sensorManager, host, alarmManager, config, params,
                         handler, wakeLock, machine),
                 createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params),
                 new DozeScreenState(wrappedService, handler, params, wakeLock),
-                createDozeScreenBrightness(context, wrappedService, sensorManager, host, handler),
+                createDozeScreenBrightness(context, wrappedService, sensorManager, host, params,
+                        handler),
                 new DozeWallpaperState(context, params)
         });
 
@@ -76,11 +77,11 @@
 
     private DozeMachine.Part createDozeScreenBrightness(Context context,
             DozeMachine.Service service, SensorManager sensorManager, DozeHost host,
-            Handler handler) {
+            DozeParameters params, Handler handler) {
         Sensor sensor = DozeSensors.findSensorWithType(sensorManager,
                 context.getString(R.string.doze_brightness_sensor_type));
         return new DozeScreenBrightness(context, service, sensorManager, sensor, host, handler,
-                new AlwaysOnDisplayPolicy(context));
+                params.getPolicy());
     }
 
     private DozeTriggers createDozeTriggers(Context context, SensorManager sensorManager,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index f72bff5d6..a0fdb9d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -21,6 +21,7 @@
 import android.view.Display;
 
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.wakelock.SettableWakeLock;
 import com.android.systemui.util.wakelock.WakeLock;
 
 /**
@@ -43,15 +44,14 @@
     private final DozeParameters mParameters;
 
     private int mPendingScreenState = Display.STATE_UNKNOWN;
-    private boolean mWakeLockHeld;
-    private WakeLock mWakeLock;
+    private SettableWakeLock mWakeLock;
 
     public DozeScreenState(DozeMachine.Service service, Handler handler,
             DozeParameters parameters, WakeLock wakeLock) {
         mDozeService = service;
         mHandler = handler;
         mParameters = parameters;
-        mWakeLock = wakeLock;
+        mWakeLock = new SettableWakeLock(wakeLock);
     }
 
     @Override
@@ -64,6 +64,7 @@
             mHandler.removeCallbacks(mApplyPendingScreenState);
 
             applyScreenState(screenState);
+            mWakeLock.setAcquired(false);
             return;
         }
 
@@ -84,9 +85,8 @@
             boolean shouldDelayTransition = newState == DozeMachine.State.DOZE_AOD
                     && mParameters.shouldControlScreenOff();
 
-            if (!mWakeLockHeld && shouldDelayTransition) {
-                mWakeLockHeld = true;
-                mWakeLock.acquire();
+            if (shouldDelayTransition) {
+                mWakeLock.setAcquired(true);
             }
 
             if (!messagePending) {
@@ -118,10 +118,7 @@
             if (DEBUG) Log.d(TAG, "setDozeScreenState(" + screenState + ")");
             mDozeService.setDozeScreenState(screenState);
             mPendingScreenState = Display.STATE_UNKNOWN;
-            if (mWakeLockHeld) {
-                mWakeLockHeld = false;
-                mWakeLock.release();
-            }
+            mWakeLock.setAcquired(false);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index aa26419..7339304 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -62,6 +62,7 @@
     public void onDestroy() {
         Dependency.get(PluginManager.class).removePluginListener(this);
         super.onDestroy();
+        mDozeMachine = null;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index f7a258a..be3e322 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -85,7 +85,7 @@
         mAllowPulseTriggers = allowPulseTriggers;
         mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
                 config, wakeLock, this::onSensor, this::onProximityFar,
-                new AlwaysOnDisplayPolicy(context));
+                dozeParameters.getPolicy());
         mUiModeManager = mContext.getSystemService(UiModeManager.class);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 07b79a2..d2d9c4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -59,9 +59,9 @@
 
     @VisibleForTesting
     protected DozeParameters(Context context) {
-        mContext = context;
+        mContext = context.getApplicationContext();
         mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
-        mAlwaysOnPolicy = new AlwaysOnDisplayPolicy(context);
+        mAlwaysOnPolicy = new AlwaysOnDisplayPolicy(mContext);
 
         mControlScreenOffAnimation = !getDisplayNeedsBlanking();
         mPowerManager = mContext.getSystemService(PowerManager.class);
@@ -243,6 +243,10 @@
         mDozeAlwaysOn = mAmbientDisplayConfiguration.alwaysOnEnabled(UserHandle.USER_CURRENT);
     }
 
+    public AlwaysOnDisplayPolicy getPolicy() {
+        return mAlwaysOnPolicy;
+    }
+
     /**
      * Parses a spec of the form `1,2,3,!5,*`. The resulting object will match numbers that are
      * listed, will not match numbers that are listed with a ! prefix, and will match / not match
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 94ac4f62..669a8c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -82,6 +82,7 @@
         mSlotWifi     = mContext.getString(com.android.internal.R.string.status_bar_wifi);
         mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet);
         mSlotVpn      = mContext.getString(com.android.internal.R.string.status_bar_vpn);
+        mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
 
         mIconController = iconController;
         mNetworkController = Dependency.get(NetworkController.class);
@@ -108,10 +109,6 @@
         return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic;
     }
 
-    private void updateActivityEnabled() {
-        mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
-    }
-
     /**
      * From SecurityController
      */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index 6683636..b6039b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -25,8 +25,10 @@
 import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED;
 import static com.android.systemui.utils.os.FakeHandler.Mode.QUEUEING;
 
+import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -41,6 +43,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.wakelock.WakeLock;
+import com.android.systemui.util.wakelock.WakeLockFake;
 import com.android.systemui.utils.os.FakeHandler;
 
 import org.junit.Before;
@@ -58,8 +61,7 @@
     FakeHandler mHandlerFake;
     @Mock
     DozeParameters mDozeParameters;
-    @Mock
-    WakeLock mWakeLock;
+    WakeLockFake mWakeLock;
 
     @Before
     public void setUp() throws Exception {
@@ -68,6 +70,7 @@
         when(mDozeParameters.getAlwaysOn()).thenReturn(true);
         mServiceFake = new DozeServiceFake();
         mHandlerFake = new FakeHandler(Looper.getMainLooper());
+        mWakeLock = new WakeLockFake();
         mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeParameters, mWakeLock);
     }
 
@@ -158,14 +161,29 @@
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mHandlerFake.dispatchQueuedMessages();
-        reset(mWakeLock);
 
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
-        verify(mWakeLock).acquire();
-        verify(mWakeLock, never()).release();
+        assertThat(mWakeLock.isHeld(), is(true));
 
         mHandlerFake.dispatchQueuedMessages();
-        verify(mWakeLock).release();
+        assertThat(mWakeLock.isHeld(), is(false));
+    }
+
+    @Test
+    public void test_releasesWakeLock_abortingLowPowerDelayed() {
+        // Transition to low power mode will be delayed to let
+        // animations play at 60 fps.
+        when(mDozeParameters.shouldControlScreenOff()).thenReturn(true);
+        mHandlerFake.setMode(QUEUEING);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mHandlerFake.dispatchQueuedMessages();
+
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
+        assertThat(mWakeLock.isHeld(), is(true));
+        mScreen.transitionTo(DOZE_AOD, FINISH);
+
+        assertThat(mWakeLock.isHeld(), is(false));
     }
 
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 2ed4516..ae14dfa 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -30,26 +30,58 @@
 
     private static final String TAG = "BinderCallsStatsService";
 
-    private static final String PERSIST_SYS_BINDER_CPU_STATS_TRACKING
-            = "persist.sys.binder_cpu_stats_tracking";
+    private static final String PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
+            = "persist.sys.binder_calls_detailed_tracking";
 
     public static void start() {
         BinderCallsStatsService service = new BinderCallsStatsService();
         ServiceManager.addService("binder_calls_stats", service);
-        // TODO Enable by default on eng/userdebug builds
-        boolean trackingEnabledDefault = false;
-        boolean trackingEnabled = SystemProperties.getBoolean(PERSIST_SYS_BINDER_CPU_STATS_TRACKING,
-                trackingEnabledDefault);
+        boolean detailedTrackingEnabled = SystemProperties.getBoolean(
+                PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false);
 
-        if (trackingEnabled) {
+        if (detailedTrackingEnabled) {
             Slog.i(TAG, "Enabled CPU usage tracking for binder calls. Controlled by "
-                    + PERSIST_SYS_BINDER_CPU_STATS_TRACKING);
-            BinderCallsStats.getInstance().setTrackingEnabled(true);
+                    + PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
+                    + " or via dumpsys binder_calls_stats --enable-detailed-tracking");
+            BinderCallsStats.getInstance().setDetailedTracking(true);
         }
     }
 
+    public static void reset() {
+        Slog.i(TAG, "Resetting stats");
+        BinderCallsStats.getInstance().reset();
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (args != null) {
+            for (final String arg : args) {
+                if ("--reset".equals(arg)) {
+                    reset();
+                    pw.println("binder_calls_stats reset.");
+                    return;
+                } else if ("--enable-detailed-tracking".equals(arg)) {
+                    SystemProperties.set(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, "1");
+                    BinderCallsStats.getInstance().setDetailedTracking(true);
+                    pw.println("Detailed tracking enabled");
+                    return;
+                } else if ("--disable-detailed-tracking".equals(arg)) {
+                    SystemProperties.set(PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, "");
+                    BinderCallsStats.getInstance().setDetailedTracking(false);
+                    pw.println("Detailed tracking disabled");
+                    return;
+                } else if ("-h".equals(arg)) {
+                    pw.println("binder_calls_stats commands:");
+                    pw.println("  --reset: Reset stats");
+                    pw.println("  --enable-detailed-tracking: Enables detailed tracking");
+                    pw.println("  --disable-detailed-tracking: Disables detailed tracking");
+                    return;
+                } else {
+                    pw.println("Unknown option: " + arg);
+                    return;
+                }
+            }
+        }
         BinderCallsStats.getInstance().dump(pw);
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9045841..137e6ab 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -14542,6 +14542,10 @@
     }
 
     void setRunningRemoteAnimation(int pid, boolean runningRemoteAnimation) {
+        if (pid == Process.myPid()) {
+            Slog.wtf(TAG, "system can't run remote animation");
+            return;
+        }
         synchronized (ActivityManagerService.this) {
             final ProcessRecord pr;
             synchronized (mPidsSelfLocked) {
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 762d4e5..c761754 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -82,6 +82,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.content.res.ResourceId;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -538,25 +539,25 @@
     }
 
     Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
-        int anim = 0;
+        int resId = ResourceId.ID_NULL;
         Context context = mContext;
         if (animAttr >= 0) {
             AttributeCache.Entry ent = getCachedAnimations(lp);
             if (ent != null) {
                 context = ent.context;
-                anim = ent.array.getResourceId(animAttr, 0);
+                resId = ent.array.getResourceId(animAttr, 0);
             }
         }
-        anim = updateToTranslucentAnimIfNeeded(anim, transit);
-        if (anim != 0) {
-            return AnimationUtils.loadAnimation(context, anim);
+        resId = updateToTranslucentAnimIfNeeded(resId, transit);
+        if (ResourceId.isValid(resId)) {
+            return AnimationUtils.loadAnimation(context, resId);
         }
         return null;
     }
 
     Animation loadAnimationRes(LayoutParams lp, int resId) {
         Context context = mContext;
-        if (resId >= 0) {
+        if (ResourceId.isValid(resId)) {
             AttributeCache.Entry ent = getCachedAnimations(lp);
             if (ent != null) {
                 context = ent.context;
@@ -567,18 +568,12 @@
     }
 
     private Animation loadAnimationRes(String packageName, int resId) {
-        int anim = 0;
-        Context context = mContext;
-        if (resId >= 0) {
+        if (ResourceId.isValid(resId)) {
             AttributeCache.Entry ent = getCachedAnimations(packageName, resId);
             if (ent != null) {
-                context = ent.context;
-                anim = resId;
+                return AnimationUtils.loadAnimation(ent.context, resId);
             }
         }
-        if (anim != 0) {
-            return AnimationUtils.loadAnimation(context, anim);
-        }
         return null;
     }
 
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index ec40a222..ffca466 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -471,11 +471,20 @@
     _emoji_zwj_sequences.update(parse_unicode_datafile(
         path.join(ucd_path, 'additions', 'emoji-zwj-sequences.txt')))
 
+    exclusions = parse_unicode_datafile(path.join(ucd_path, 'additions', 'emoji-exclusions.txt'))
+    _emoji_sequences = remove_emoji_exclude(_emoji_sequences, exclusions)
+    _emoji_zwj_sequences = remove_emoji_exclude(_emoji_zwj_sequences, exclusions)
+    _emoji_variation_sequences = remove_emoji_variation_exclude(_emoji_variation_sequences, exclusions)
+
+def remove_emoji_variation_exclude(source, items):
+    return source.difference(items.keys())
+
+def remove_emoji_exclude(source, items):
+    return {k: v for k, v in source.items() if k not in items}
 
 def flag_sequence(territory_code):
     return tuple(0x1F1E6 + ord(ch) - ord('A') for ch in territory_code)
 
-
 UNSUPPORTED_FLAGS = frozenset({
     flag_sequence('BL'), flag_sequence('BQ'), flag_sequence('DG'),
     flag_sequence('EA'), flag_sequence('EH'), flag_sequence('FK'),
@@ -522,8 +531,6 @@
 ZWJ_IDENTICALS = {
     # KISS
     (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468): 0x1F48F,
-    # COUPLE WITH HEART
-    (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F468): 0x1F491,
     # FAMILY
     (0x1F468, 0x200D, 0x1F469, 0x200D, 0x1F466): 0x1F46A,
 }
@@ -576,6 +583,8 @@
     (0x1F9DD, FEMALE_SIGN), # ELF
     (0x1F9DE, FEMALE_SIGN), # GENIE
     (0x1F9DF, FEMALE_SIGN), # ZOMBIE
+    (0X1F9B8, FEMALE_SIGN), # SUPERVILLAIN
+    (0x1F9B9, FEMALE_SIGN), # SUPERHERO
 ]
 
 def is_fitzpatrick_modifier(cp):