Merge "More formatting fixes for statsd"
diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java b/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java
similarity index 73%
rename from apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
rename to apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java
index 73e1724..fc6302e 100644
--- a/apct-tests/perftests/core/src/android/text/PrecomputedTextMemoryUsageTest.java
+++ b/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java
@@ -45,7 +45,7 @@
 
 @LargeTest
 @RunWith(AndroidJUnit4.class)
-public class PrecomputedTextMemoryUsageTest {
+public class MeasuredTextMemoryUsageTest {
     private static final int WORD_LENGTH = 9;  // Random word has 9 characters.
     private static final boolean NO_STYLE_TEXT = false;
 
@@ -53,7 +53,7 @@
 
     private static int TRIAL_COUNT = 100;
 
-    public PrecomputedTextMemoryUsageTest() {}
+    public MeasuredTextMemoryUsageTest() {}
 
     private TextPerfUtils mTextUtil = new TextPerfUtils();
 
@@ -77,16 +77,13 @@
     @Test
     public void testMemoryUsage_NoHyphenation() {
         int[] memories = new int[TRIAL_COUNT];
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+        // Report median of randomly generated MeasuredText.
+        for (int i = 0; i < TRIAL_COUNT; ++i) {
+            memories[i] = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                 .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                 .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                .build();
-
-        // Report median of randomly generated PrecomputedText.
-        for (int i = 0; i < TRIAL_COUNT; ++i) {
-            memories[i] = PrecomputedText.create(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param)
-                .getMemoryUsage();
+                .build().getMemoryUsage();
         }
         reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation");
     }
@@ -94,16 +91,13 @@
     @Test
     public void testMemoryUsage_Hyphenation() {
         int[] memories = new int[TRIAL_COUNT];
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+        // Report median of randomly generated MeasuredText.
+        for (int i = 0; i < TRIAL_COUNT; ++i) {
+            memories[i] = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                 .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
                 .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
-                .build();
-
-        // Report median of randomly generated PrecomputedText.
-        for (int i = 0; i < TRIAL_COUNT; ++i) {
-            memories[i] = PrecomputedText.create(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param)
-                .getMemoryUsage();
+                .build().getMemoryUsage();
         }
         reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation");
     }
@@ -111,16 +105,13 @@
     @Test
     public void testMemoryUsage_NoHyphenation_WidthOnly() {
         int[] memories = new int[TRIAL_COUNT];
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+        // Report median of randomly generated MeasuredText.
+        for (int i = 0; i < TRIAL_COUNT; ++i) {
+            memories[i] = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                 .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                 .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                .build();
-
-        // Report median of randomly generated PrecomputedText.
-        for (int i = 0; i < TRIAL_COUNT; ++i) {
-            CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
-            memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length())
-                .getMemoryUsage();
+                .build(false /* width only */).getMemoryUsage();
         }
         reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation_WidthOnly");
     }
@@ -128,16 +119,13 @@
     @Test
     public void testMemoryUsage_Hyphenatation_WidthOnly() {
         int[] memories = new int[TRIAL_COUNT];
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
+        // Report median of randomly generated MeasuredText.
+        for (int i = 0; i < TRIAL_COUNT; ++i) {
+            memories[i] = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                 .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
                 .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
-                .build();
-
-        // Report median of randomly generated PrecomputedText.
-        for (int i = 0; i < TRIAL_COUNT; ++i) {
-            CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
-            memories[i] = PrecomputedText.createWidthOnly(cs, param, 0, cs.length())
-                .getMemoryUsage();
+                .build(false /* width only */).getMemoryUsage();
         }
         reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation_WidthOnly");
     }
diff --git a/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java
similarity index 66%
rename from apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
rename to apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java
index 1cd0ae1..98f2bd5 100644
--- a/apct-tests/perftests/core/src/android/text/PrecomputedTextPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java
@@ -42,7 +42,7 @@
 
 @LargeTest
 @RunWith(AndroidJUnit4.class)
-public class PrecomputedTextPerfTest {
+public class MeasuredTextPerfTest {
     private static final int WORD_LENGTH = 9;  // Random word has 9 characters.
     private static final int WORDS_IN_LINE = 8;  // Roughly, 8 words in a line.
     private static final boolean NO_STYLE_TEXT = false;
@@ -51,7 +51,7 @@
     private static TextPaint PAINT = new TextPaint();
     private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
 
-    public PrecomputedTextPerfTest() {}
+    public MeasuredTextPerfTest() {}
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
@@ -66,136 +66,120 @@
     @Test
     public void testCreate_NoStyled_Hyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .build(true /* do full layout */);
         }
     }
 
     @Test
     public void testCreate_NoStyled_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .build(true /* do full layout */);
         }
     }
 
     @Test
     public void testCreate_NoStyled_Hyphenation_WidthOnly() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .build(false /* width only */);
         }
     }
 
     @Test
     public void testCreate_NoStyled_NoHyphenation_WidthOnly() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .build(false /* width only */);
         }
     }
 
     @Test
     public void testCreate_Styled_Hyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .build(true /* do full layout */);
         }
     }
 
     @Test
     public void testCreate_Styled_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .build(true /* do full layout */);
         }
     }
 
     @Test
     public void testCreate_Styled_Hyphenation_WidthOnly() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .build(false /* width only */);
         }
     }
 
     @Test
     public void testCreate_Styled_NoHyphenation_WidthOnly() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
-                .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
-                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
-                .build();
-
         while (state.keepRunning()) {
             state.pauseTiming();
             final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             state.resumeTiming();
 
-            PrecomputedText.create(text, param);
+            new MeasuredText.Builder(text, PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .build(false /* width only */);
         }
     }
 }
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
index 8823af1..231aaf2 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -63,18 +63,6 @@
         mTextUtil.resetRandom(0 /* seed */);
     }
 
-    private PrecomputedText makeMeasured(CharSequence text, TextPaint paint) {
-        PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint).build();
-        return PrecomputedText.create(text, param);
-    }
-
-    private PrecomputedText makeMeasured(CharSequence text, TextPaint paint, int strategy,
-                                      int frequency) {
-        PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint)
-                .setHyphenationFrequency(frequency).setBreakStrategy(strategy).build();
-        return PrecomputedText.create(text, param);
-    }
-
     @Test
     public void testCreate_FixedText_NoStyle_Greedy_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
@@ -163,16 +151,18 @@
     }
 
     @Test
-    public void testCreate_PrecomputedText_NoStyled_Greedy_NoHyphenation() {
+    public void testCreate_MeasuredText_NoStyled_Greedy_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
-                    Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .build();
             state.resumeTiming();
 
-            StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH)
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                     .build();
@@ -180,16 +170,18 @@
     }
 
     @Test
-    public void testCreate_PrecomputedText_NoStyled_Greedy_Hyphenation() {
+    public void testCreate_MeasuredText_NoStyled_Greedy_Hyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
-                    Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NORMAL);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .build();
             state.resumeTiming();
 
-            StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH)
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                     .build();
@@ -197,16 +189,18 @@
     }
 
     @Test
-    public void testCreate_PrecomputedText_NoStyled_Balanced_NoHyphenation() {
+    public void testCreate_MeasuredText_NoStyled_Balanced_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
-                    Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NONE);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .build();
             state.resumeTiming();
 
-            StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH)
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
                     .build();
@@ -214,16 +208,18 @@
     }
 
     @Test
-    public void testCreate_PrecomputedText_NoStyled_Balanced_Hyphenation() {
+    public void testCreate_MeasuredText_NoStyled_Balanced_Hyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
-                    Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NORMAL);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
+                    .build();
             state.resumeTiming();
 
-            StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH)
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
                     .build();
@@ -231,16 +227,18 @@
     }
 
     @Test
-    public void testCreate_PrecomputedText_Styled_Greedy_NoHyphenation() {
+    public void testCreate_MeasuredText_Styled_Greedy_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT,
-                    Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT)
+                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
+                    .build();
             state.resumeTiming();
 
-            StaticLayout.Builder.obtain(text, 0, text.getText().length(), PAINT, TEXT_WIDTH)
+            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                     .build();
@@ -330,16 +328,15 @@
     }
 
     @Test
-    public void testDraw_PrecomputedText_Styled() {
+    public void testDraw_MeasuredText_Styled() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
             final StaticLayout layout =
-                    StaticLayout.Builder.obtain(
-                            text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build();
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
             state.resumeTiming();
 
@@ -348,16 +345,15 @@
     }
 
     @Test
-    public void testDraw_PrecomputedText_NoStyled() {
+    public void testDraw_MeasuredText_NoStyled() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
             final StaticLayout layout =
-                    StaticLayout.Builder.obtain(
-                            text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build();
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
             state.resumeTiming();
 
@@ -366,16 +362,15 @@
     }
 
     @Test
-    public void testDraw_PrecomputedText_Styled_WithoutCache() {
+    public void testDraw_MeasuredText_Styled_WithoutCache() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
             final StaticLayout layout =
-                    StaticLayout.Builder.obtain(
-                            text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build();
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
@@ -385,16 +380,15 @@
     }
 
     @Test
-    public void testDraw_PrecomputedText_NoStyled_WithoutCache() {
+    public void testDraw_MeasuredText_NoStyled_WithoutCache() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final PrecomputedText text = makeMeasured(
-                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
+            final MeasuredText text = new MeasuredText.Builder(
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
             final StaticLayout layout =
-                    StaticLayout.Builder.obtain(
-                            text, 0, text.getText().length(), PAINT, TEXT_WIDTH).build();
+                    StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
             Canvas.freeTextLayoutCaches();
             state.resumeTiming();
diff --git a/api/current.txt b/api/current.txt
index 53f503a..df18f10 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -13839,7 +13839,6 @@
     method public int breakText(java.lang.String, boolean, float, float[]);
     method public void clearShadowLayer();
     method public float descent();
-    method public boolean equalsForTextMeasurement(android.graphics.Paint);
     method public int getAlpha();
     method public int getColor();
     method public android.graphics.ColorFilter getColorFilter();
@@ -26757,7 +26756,7 @@
     field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
     field public static final java.lang.String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
     field public static final java.lang.String ACTION_RESTRICT_BACKGROUND_CHANGED = "android.net.conn.RESTRICT_BACKGROUND_CHANGED";
-    field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
+    field public static final deprecated java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
     field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
     field public static final java.lang.String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
     field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_URL = "android.net.extra.CAPTIVE_PORTAL_URL";
@@ -27033,6 +27032,7 @@
     field public static final int NET_CAPABILITY_CBS = 5; // 0x5
     field public static final int NET_CAPABILITY_DUN = 2; // 0x2
     field public static final int NET_CAPABILITY_EIMS = 10; // 0xa
+    field public static final int NET_CAPABILITY_FOREGROUND = 19; // 0x13
     field public static final int NET_CAPABILITY_FOTA = 3; // 0x3
     field public static final int NET_CAPABILITY_IA = 7; // 0x7
     field public static final int NET_CAPABILITY_IMS = 4; // 0x4
@@ -27041,6 +27041,7 @@
     field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
     field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd
     field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12
+    field public static final int NET_CAPABILITY_NOT_SUSPENDED = 21; // 0x15
     field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf
     field public static final int NET_CAPABILITY_RCS = 8; // 0x8
     field public static final int NET_CAPABILITY_SUPL = 1; // 0x1
@@ -28560,6 +28561,7 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.net.wifi.rtt.RangingResult> CREATOR;
     field public static final int STATUS_FAIL = 1; // 0x1
+    field public static final int STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC = 2; // 0x2
     field public static final int STATUS_SUCCESS = 0; // 0x0
   }
 
@@ -38685,14 +38687,14 @@
   }
 
   public final class DateTransformation implements android.os.Parcelable android.service.autofill.Transformation {
-    ctor public DateTransformation(android.view.autofill.AutofillId, java.text.DateFormat);
+    ctor public DateTransformation(android.view.autofill.AutofillId, android.icu.text.DateFormat);
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.DateTransformation> CREATOR;
   }
 
   public final class DateValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
-    ctor public DateValueSanitizer(java.text.DateFormat);
+    ctor public DateValueSanitizer(android.icu.text.DateFormat);
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.autofill.DateValueSanitizer> CREATOR;
@@ -38703,7 +38705,7 @@
   }
 
   public static final class FieldClassification.Match {
-    method public java.lang.String getRemoteId();
+    method public java.lang.String getCategoryId();
     method public float getScore();
   }
 
@@ -38868,6 +38870,7 @@
     method public int describeContents();
     method public java.lang.String getFieldClassificationAlgorithm();
     method public java.lang.String getId();
+    method public static int getMaxCategoryCount();
     method public static int getMaxFieldClassificationIdsSize();
     method public static int getMaxUserDataSize();
     method public static int getMaxValueLength();
@@ -43229,6 +43232,36 @@
     method public boolean isAllowed(char);
   }
 
+  public class MeasuredText implements android.text.Spanned {
+    method public char charAt(int);
+    method public int getBreakStrategy();
+    method public int getEnd();
+    method public int getHyphenationFrequency();
+    method public android.text.TextPaint getPaint();
+    method public int getParagraphCount();
+    method public int getParagraphEnd(int);
+    method public int getParagraphStart(int);
+    method public int getSpanEnd(java.lang.Object);
+    method public int getSpanFlags(java.lang.Object);
+    method public int getSpanStart(java.lang.Object);
+    method public <T> T[] getSpans(int, int, java.lang.Class<T>);
+    method public int getStart();
+    method public java.lang.CharSequence getText();
+    method public android.text.TextDirectionHeuristic getTextDir();
+    method public int length();
+    method public int nextSpanTransition(int, int, java.lang.Class);
+    method public java.lang.CharSequence subSequence(int, int);
+  }
+
+  public static final class MeasuredText.Builder {
+    ctor public MeasuredText.Builder(java.lang.CharSequence, android.text.TextPaint);
+    method public android.text.MeasuredText build();
+    method public android.text.MeasuredText.Builder setBreakStrategy(int);
+    method public android.text.MeasuredText.Builder setHyphenationFrequency(int);
+    method public android.text.MeasuredText.Builder setRange(int, int);
+    method public android.text.MeasuredText.Builder setTextDirection(android.text.TextDirectionHeuristic);
+  }
+
   public abstract interface NoCopySpan {
   }
 
@@ -43240,31 +43273,6 @@
     method public abstract int getSpanTypeId();
   }
 
-  public class PrecomputedText {
-    method public static android.text.PrecomputedText create(java.lang.CharSequence, android.text.PrecomputedText.Params);
-    method public int getParagraphCount();
-    method public int getParagraphEnd(int);
-    method public int getParagraphStart(int);
-    method public android.text.PrecomputedText.Params getParams();
-    method public java.lang.CharSequence getText();
-  }
-
-  public static class PrecomputedText.Params {
-    method public int getBreakStrategy();
-    method public int getHyphenationFrequency();
-    method public android.text.TextDirectionHeuristic getTextDirection();
-    method public android.text.TextPaint getTextPaint();
-    method public boolean sameTextMetrics(android.text.PrecomputedText.Params);
-  }
-
-  public static class PrecomputedText.Params.Builder {
-    ctor public PrecomputedText.Params.Builder(android.text.TextPaint);
-    method public android.text.PrecomputedText.Params build();
-    method public android.text.PrecomputedText.Params.Builder setBreakStrategy(int);
-    method public android.text.PrecomputedText.Params.Builder setHyphenationFrequency(int);
-    method public android.text.PrecomputedText.Params.Builder setTextDirection(android.text.TextDirectionHeuristic);
-  }
-
   public class Selection {
     method public static boolean extendDown(android.text.Spannable, android.text.Layout);
     method public static boolean extendLeft(android.text.Spannable, android.text.Layout);
@@ -43396,7 +43404,6 @@
 
   public static final class StaticLayout.Builder {
     method public android.text.StaticLayout build();
-    method public static android.text.StaticLayout.Builder obtain(android.text.PrecomputedText, int, int, android.text.TextPaint, int);
     method public static android.text.StaticLayout.Builder obtain(java.lang.CharSequence, int, int, android.text.TextPaint, int);
     method public android.text.StaticLayout.Builder setAlignment(android.text.Layout.Alignment);
     method public android.text.StaticLayout.Builder setBreakStrategy(int);
@@ -53648,7 +53655,6 @@
     method public final android.content.res.ColorStateList getTextColors();
     method public java.util.Locale getTextLocale();
     method public android.os.LocaleList getTextLocales();
-    method public android.text.PrecomputedText.Params getTextMetricsParams();
     method public float getTextScaleX();
     method public float getTextSize();
     method public int getTotalPaddingBottom();
@@ -53754,8 +53760,6 @@
     method public final void setMovementMethod(android.text.method.MovementMethod);
     method public void setOnEditorActionListener(android.widget.TextView.OnEditorActionListener);
     method public void setPaintFlags(int);
-    method public void setPrecomputedTextAndParams(android.text.PrecomputedText);
-    method public void setPrecomputedTextOrThrow(android.text.PrecomputedText);
     method public void setPrivateImeOptions(java.lang.String);
     method public void setRawInputType(int);
     method public void setScroller(android.widget.Scroller);
@@ -53780,7 +53784,6 @@
     method public final void setTextKeepState(java.lang.CharSequence, android.widget.TextView.BufferType);
     method public void setTextLocale(java.util.Locale);
     method public void setTextLocales(android.os.LocaleList);
-    method public void setTextMetricsParams(android.text.PrecomputedText.Params);
     method public void setTextScaleX(float);
     method public void setTextSize(float);
     method public void setTextSize(int, float);
diff --git a/api/system-current.txt b/api/system-current.txt
index a9c0c3a..8001ee3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3605,11 +3605,14 @@
   }
 
   public abstract class HwBinder implements android.os.IHwBinder {
+    ctor public HwBinder();
     method public static final void configureRpcThreadpool(long, boolean);
     method public static void enableInstrumentation();
     method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String) throws java.util.NoSuchElementException, android.os.RemoteException;
     method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String, boolean) throws java.util.NoSuchElementException, android.os.RemoteException;
     method public static final void joinRpcThreadpool();
+    method public abstract void onTransact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+    method public final void registerService(java.lang.String) throws android.os.RemoteException;
     method public final void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
   }
 
@@ -4161,6 +4164,8 @@
     method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
     field public static final java.lang.String AUTOFILL_COMPAT_ALLOWED_PACKAGES = "autofill_compat_allowed_packages";
     field public static final java.lang.String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
+    field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent";
+    field public static final java.lang.String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
     field public static final java.lang.String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
     field public static final java.lang.String THEATER_MODE_ON = "theater_mode_on";
     field public static final java.lang.String WEBVIEW_MULTIPROCESS = "webview_multiprocess";
@@ -4172,6 +4177,7 @@
     method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean);
     method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
     field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
+    field public static final java.lang.String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size";
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
diff --git a/api/test-current.txt b/api/test-current.txt
index b02da04..9bfc105 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -541,6 +541,7 @@
     field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
     field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
     field public static final java.lang.String AUTOFILL_SERVICE = "autofill_service";
+    field public static final java.lang.String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size";
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
     field public static final java.lang.String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
@@ -1047,6 +1048,15 @@
 
 }
 
+package android.view.animation {
+
+  public class AnimationUtils {
+    method public static void lockAnimationClock(long);
+    method public static void unlockAnimationClock();
+  }
+
+}
+
 package android.view.autofill {
 
   public final class AutofillId implements android.os.Parcelable {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 813335a..c04e61b 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -172,6 +172,8 @@
             } else if (opt.equals("--no_window_animation")
                     || opt.equals("--no-window-animation")) {
                 instrument.noWindowAnimation = true;
+            } else if (opt.equals("--no-hidden-api-checks")) {
+                instrument.disableHiddenApiChecks = true;
             } else if (opt.equals("--user")) {
                 instrument.userId = parseUserArg(nextArgRequired());
             } else if (opt.equals("--abi")) {
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index d79b1a6..0dade0b 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -73,12 +73,17 @@
     boolean protoFile = false;  // write proto to a file
     String logPath = null;
     public boolean noWindowAnimation = false;
+    public boolean disableHiddenApiChecks = false;
     public String abi = null;
     public int userId = UserHandle.USER_CURRENT;
     public Bundle args = new Bundle();
     // Required
     public String componentNameArg;
 
+    // Disable hidden API checks for the newly started instrumentation.
+    // Must be kept in sync with ActivityManagerService.
+    private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
+
     /**
      * Construct the instrument command runner.
      */
@@ -475,7 +480,8 @@
             }
 
             // Start the instrumentation
-            if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId,
+            int flags = disableHiddenApiChecks ? INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS : 0;
+            if (!mAm.startInstrumentation(cn, profileFile, flags, args, watcher, connection, userId,
                         abi)) {
                 throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
             }
diff --git a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
index 02f1ce7..fde17bd 100644
--- a/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
+++ b/cmds/incident_helper/src/parsers/CpuFreqParser.cpp
@@ -62,15 +62,15 @@
     ProtoOutputStream proto;
 
     long jiffyHz = sysconf(_SC_CLK_TCK);
-    proto.write(CpuFreq::JIFFY_HZ, (int)jiffyHz);
+    proto.write(CpuFreqProto::JIFFY_HZ, (int)jiffyHz);
 
     for (int i=0; i<numCpus; i++) {
-        long long token = proto.start(CpuFreq::CPU_FREQS);
-        proto.write(CpuFreqStats::CPU_NAME, header[i+1]);
+        long long token = proto.start(CpuFreqProto::CPU_FREQS);
+        proto.write(CpuFreqProto::Stats::CPU_NAME, header[i+1]);
         for (vector<pair<int, long long>>::iterator it = cpucores[i].begin(); it != cpucores[i].end(); it++) {
-            long long stateToken = proto.start(CpuFreqStats::TIMES);
-            proto.write(CpuFreqStats::TimeInState::STATE_KHZ, it->first);
-            proto.write(CpuFreqStats::TimeInState::TIME_JIFFY, it->second);
+            long long stateToken = proto.start(CpuFreqProto::Stats::TIMES);
+            proto.write(CpuFreqProto::Stats::TimeInState::STATE_KHZ, it->first);
+            proto.write(CpuFreqProto::Stats::TimeInState::TIME_JIFFY, it->second);
             proto.end(stateToken);
         }
         proto.end(token);
diff --git a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
index d73de54..b2b431c 100644
--- a/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
+++ b/cmds/incident_helper/src/parsers/CpuInfoParser.cpp
@@ -54,11 +54,11 @@
     bool nextToUsage = false;
 
     ProtoOutputStream proto;
-    Table table(CpuInfo::Task::_FIELD_NAMES, CpuInfo::Task::_FIELD_IDS, CpuInfo::Task::_FIELD_COUNT);
-    table.addEnumTypeMap("s", CpuInfo::Task::_ENUM_STATUS_NAMES,
-            CpuInfo::Task::_ENUM_STATUS_VALUES, CpuInfo::Task::_ENUM_STATUS_COUNT);
-    table.addEnumTypeMap("pcy", CpuInfo::Task::_ENUM_POLICY_NAMES,
-            CpuInfo::Task::_ENUM_POLICY_VALUES, CpuInfo::Task::_ENUM_POLICY_COUNT);
+    Table table(CpuInfoProto::Task::_FIELD_NAMES, CpuInfoProto::Task::_FIELD_IDS, CpuInfoProto::Task::_FIELD_COUNT);
+    table.addEnumTypeMap("s", CpuInfoProto::Task::_ENUM_STATUS_NAMES,
+            CpuInfoProto::Task::_ENUM_STATUS_VALUES, CpuInfoProto::Task::_ENUM_STATUS_COUNT);
+    table.addEnumTypeMap("pcy", CpuInfoProto::Task::_ENUM_POLICY_NAMES,
+            CpuInfoProto::Task::_ENUM_POLICY_VALUES, CpuInfoProto::Task::_ENUM_POLICY_COUNT);
 
     // parse line by line
     while (reader.readLine(&line)) {
@@ -67,33 +67,33 @@
         nline++;
 
         if (stripPrefix(&line, "Tasks:")) {
-            writeSuffixLine(&proto, CpuInfo::TASK_STATS, line, COMMA_DELIMITER,
-                CpuInfo::TaskStats::_FIELD_COUNT,
-                CpuInfo::TaskStats::_FIELD_NAMES,
-                CpuInfo::TaskStats::_FIELD_IDS);
+            writeSuffixLine(&proto, CpuInfoProto::TASK_STATS, line, COMMA_DELIMITER,
+                CpuInfoProto::TaskStats::_FIELD_COUNT,
+                CpuInfoProto::TaskStats::_FIELD_NAMES,
+                CpuInfoProto::TaskStats::_FIELD_IDS);
             continue;
         }
         if (stripPrefix(&line, "Mem:")) {
-            writeSuffixLine(&proto, CpuInfo::MEM, line, COMMA_DELIMITER,
-                CpuInfo::MemStats::_FIELD_COUNT,
-                CpuInfo::MemStats::_FIELD_NAMES,
-                CpuInfo::MemStats::_FIELD_IDS);
+            writeSuffixLine(&proto, CpuInfoProto::MEM, line, COMMA_DELIMITER,
+                CpuInfoProto::MemStats::_FIELD_COUNT,
+                CpuInfoProto::MemStats::_FIELD_NAMES,
+                CpuInfoProto::MemStats::_FIELD_IDS);
             continue;
         }
         if (stripPrefix(&line, "Swap:")) {
-            writeSuffixLine(&proto, CpuInfo::SWAP, line, COMMA_DELIMITER,
-                CpuInfo::MemStats::_FIELD_COUNT,
-                CpuInfo::MemStats::_FIELD_NAMES,
-                CpuInfo::MemStats::_FIELD_IDS);
+            writeSuffixLine(&proto, CpuInfoProto::SWAP, line, COMMA_DELIMITER,
+                CpuInfoProto::MemStats::_FIELD_COUNT,
+                CpuInfoProto::MemStats::_FIELD_NAMES,
+                CpuInfoProto::MemStats::_FIELD_IDS);
             nextToSwap = true;
             continue;
         }
 
         if (nextToSwap) {
-            writeSuffixLine(&proto, CpuInfo::CPU_USAGE, line, DEFAULT_WHITESPACE,
-                CpuInfo::CpuUsage::_FIELD_COUNT,
-                CpuInfo::CpuUsage::_FIELD_NAMES,
-                CpuInfo::CpuUsage::_FIELD_IDS);
+            writeSuffixLine(&proto, CpuInfoProto::CPU_USAGE, line, DEFAULT_WHITESPACE,
+                CpuInfoProto::CpuUsage::_FIELD_COUNT,
+                CpuInfoProto::CpuUsage::_FIELD_NAMES,
+                CpuInfoProto::CpuUsage::_FIELD_IDS);
             nextToUsage = true;
             nextToSwap = false;
             continue;
@@ -138,7 +138,7 @@
             continue;
         }
 
-        long long token = proto.start(CpuInfo::TASKS);
+        long long token = proto.start(CpuInfoProto::TASKS);
         for (int i=0; i<(int)record.size(); i++) {
             if (!table.insertField(&proto, header[i], record[i])) {
                 fprintf(stderr, "[%s]Line %d fails to insert field %s with value %s\n",
diff --git a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
index cae51ab..28816ea 100644
--- a/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
+++ b/cmds/incident_helper/src/parsers/KernelWakesParser.cpp
@@ -33,7 +33,9 @@
     int nline = 0;
 
     ProtoOutputStream proto;
-    Table table(WakeupSourceProto::_FIELD_NAMES, WakeupSourceProto::_FIELD_IDS, WakeupSourceProto::_FIELD_COUNT);
+    Table table(KernelWakeSourcesProto::WakeupSource::_FIELD_NAMES,
+            KernelWakeSourcesProto::WakeupSource::_FIELD_IDS,
+            KernelWakeSourcesProto::WakeupSource::_FIELD_COUNT);
 
     // parse line by line
     while (reader.readLine(&line)) {
@@ -57,7 +59,7 @@
             continue;
         }
 
-        long long token = proto.start(KernelWakeSources::WAKEUP_SOURCES);
+        long long token = proto.start(KernelWakeSourcesProto::WAKEUP_SOURCES);
         for (int i=0; i<(int)record.size(); i++) {
             if (!table.insertField(&proto, header[i], record[i])) {
                 fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
diff --git a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
index f1b93ff..45a0e7b 100644
--- a/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
+++ b/cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
@@ -33,7 +33,9 @@
     header_t blockHeader;
 
     ProtoOutputStream proto;
-    Table table(BlockProto::_FIELD_NAMES, BlockProto::_FIELD_IDS, BlockProto::_FIELD_COUNT);
+    Table table(PageTypeInfoProto::Block::_FIELD_NAMES,
+            PageTypeInfoProto::Block::_FIELD_IDS,
+            PageTypeInfoProto::Block::_FIELD_COUNT);
 
     while (reader.readLine(&line)) {
         if (line.empty()) {
@@ -44,11 +46,11 @@
 
         if (stripPrefix(&line, "Page block order:")) {
             pageBlockOrder = toInt(line);
-            proto.write(PageTypeInfo::PAGE_BLOCK_ORDER, pageBlockOrder);
+            proto.write(PageTypeInfoProto::PAGE_BLOCK_ORDER, pageBlockOrder);
             continue;
         }
         if (stripPrefix(&line, "Pages per block:")) {
-            proto.write(PageTypeInfo::PAGES_PER_BLOCK, toInt(line));
+            proto.write(PageTypeInfoProto::PAGES_PER_BLOCK, toInt(line));
             continue;
         }
         if (stripPrefix(&line, "Free pages count per migrate type at order")) {
@@ -62,14 +64,14 @@
 
         record_t record = parseRecord(line, COMMA_DELIMITER);
         if (migrateTypeSession && record.size() == 3) {
-            long long token = proto.start(PageTypeInfo::MIGRATE_TYPES);
+            long long token = proto.start(PageTypeInfoProto::MIGRATE_TYPES);
             // expect part 0 starts with "Node"
             if (stripPrefix(&record[0], "Node")) {
-                proto.write(MigrateTypeProto::NODE, toInt(record[0]));
+                proto.write(PageTypeInfoProto::MigrateType::NODE, toInt(record[0]));
             } else return BAD_VALUE;
             // expect part 1 starts with "zone"
             if (stripPrefix(&record[1], "zone")) {
-                proto.write(MigrateTypeProto::ZONE, record[1]);
+                proto.write(PageTypeInfoProto::MigrateType::ZONE, record[1]);
             } else return BAD_VALUE;
             // expect part 2 starts with "type"
             if (stripPrefix(&record[2], "type")) {
@@ -83,22 +85,22 @@
                 int pageCountsSize = pageBlockOrder + 2;
                 if ((int)pageCounts.size() != pageCountsSize) return BAD_VALUE;
 
-                proto.write(MigrateTypeProto::TYPE, pageCounts[0]);
+                proto.write(PageTypeInfoProto::MigrateType::TYPE, pageCounts[0]);
                 for (auto i=1; i<pageCountsSize; i++) {
-                    proto.write(MigrateTypeProto::FREE_PAGES_COUNT, toInt(pageCounts[i]));
+                    proto.write(PageTypeInfoProto::MigrateType::FREE_PAGES_COUNT, toInt(pageCounts[i]));
                 }
             } else return BAD_VALUE;
 
             proto.end(token);
         } else if (!blockHeader.empty() && record.size() == 2) {
-            long long token = proto.start(PageTypeInfo::BLOCKS);
+            long long token = proto.start(PageTypeInfoProto::BLOCKS);
             if (stripPrefix(&record[0], "Node")) {
-                proto.write(BlockProto::NODE, toInt(record[0]));
+                proto.write(PageTypeInfoProto::Block::NODE, toInt(record[0]));
             } else return BAD_VALUE;
 
             if (stripPrefix(&record[1], "zone")) {
                 record_t blockCounts = parseRecord(record[1]);
-                proto.write(BlockProto::ZONE, blockCounts[0]);
+                proto.write(PageTypeInfoProto::Block::ZONE, blockCounts[0]);
 
                 for (size_t i=0; i<blockHeader.size(); i++) {
                     if (!table.insertField(&proto, blockHeader[i], blockCounts[i+1])) {
diff --git a/cmds/incident_helper/src/parsers/ProcrankParser.cpp b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
index a4eb0fd..c1c458e 100644
--- a/cmds/incident_helper/src/parsers/ProcrankParser.cpp
+++ b/cmds/incident_helper/src/parsers/ProcrankParser.cpp
@@ -33,7 +33,7 @@
     int nline = 0;
 
     ProtoOutputStream proto;
-    Table table(ProcessProto::_FIELD_NAMES, ProcessProto::_FIELD_IDS, ProcessProto::_FIELD_COUNT);
+    Table table(ProcrankProto::Process::_FIELD_NAMES, ProcrankProto::Process::_FIELD_IDS, ProcrankProto::Process::_FIELD_COUNT);
     string zram, ram, total;
 
     // parse line by line
@@ -66,7 +66,7 @@
             continue;
         }
 
-        long long token = proto.start(Procrank::PROCESSES);
+        long long token = proto.start(ProcrankProto::PROCESSES);
         for (int i=0; i<(int)record.size(); i++) {
             if (!table.insertField(&proto, header[i], record[i])) {
                 fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
@@ -77,23 +77,23 @@
     }
 
     // add summary
-    long long token = proto.start(Procrank::SUMMARY);
+    long long token = proto.start(ProcrankProto::SUMMARY);
     if (!total.empty()) {
         record = parseRecord(total);
-        long long token = proto.start(SummaryProto::TOTAL);
+        long long token = proto.start(ProcrankProto::Summary::TOTAL);
         for (int i=(int)record.size(); i>0; i--) {
             table.insertField(&proto, header[header.size() - i].c_str(), record[record.size() - i].c_str());
         }
         proto.end(token);
     }
     if (!zram.empty()) {
-        long long token = proto.start(SummaryProto::ZRAM);
-        proto.write(ZramProto::RAW_TEXT, zram);
+        long long token = proto.start(ProcrankProto::Summary::ZRAM);
+        proto.write(ProcrankProto::Summary::Zram::RAW_TEXT, zram);
         proto.end(token);
     }
     if (!ram.empty()) {
-        long long token = proto.start(SummaryProto::RAM);
-        proto.write(RamProto::RAW_TEXT, ram);
+        long long token = proto.start(ProcrankProto::Summary::RAM);
+        proto.write(ProcrankProto::Summary::Ram::RAW_TEXT, ram);
         proto.end(token);
     }
     proto.end(token);
diff --git a/cmds/incident_helper/src/parsers/PsParser.cpp b/cmds/incident_helper/src/parsers/PsParser.cpp
index e9014ca..420775f 100644
--- a/cmds/incident_helper/src/parsers/PsParser.cpp
+++ b/cmds/incident_helper/src/parsers/PsParser.cpp
@@ -33,12 +33,12 @@
     int diff = 0;
 
     ProtoOutputStream proto;
-    Table table(PsDumpProto::Process::_FIELD_NAMES, PsDumpProto::Process::_FIELD_IDS, PsDumpProto::Process::_FIELD_COUNT);
+    Table table(PsProto::Process::_FIELD_NAMES, PsProto::Process::_FIELD_IDS, PsProto::Process::_FIELD_COUNT);
     const char* pcyNames[] = { "fg", "bg", "ta" };
-    const int pcyValues[] = {PsDumpProto::Process::POLICY_FG, PsDumpProto::Process::POLICY_BG, PsDumpProto::Process::POLICY_TA};
+    const int pcyValues[] = {PsProto::Process::POLICY_FG, PsProto::Process::POLICY_BG, PsProto::Process::POLICY_TA};
     table.addEnumTypeMap("pcy", pcyNames, pcyValues, 3);
     const char* sNames[] = { "D", "R", "S", "T", "t", "X", "Z" };
-    const int sValues[] = {PsDumpProto::Process::STATE_D, PsDumpProto::Process::STATE_R, PsDumpProto::Process::STATE_S, PsDumpProto::Process::STATE_T, PsDumpProto::Process::STATE_TRACING, PsDumpProto::Process::STATE_X, PsDumpProto::Process::STATE_Z};
+    const int sValues[] = {PsProto::Process::STATE_D, PsProto::Process::STATE_R, PsProto::Process::STATE_S, PsProto::Process::STATE_T, PsProto::Process::STATE_TRACING, PsProto::Process::STATE_X, PsProto::Process::STATE_Z};
     table.addEnumTypeMap("s", sNames, sValues, 7);
 
     // Parse line by line
@@ -71,7 +71,7 @@
             continue;
         }
 
-        long long token = proto.start(PsDumpProto::PROCESSES);
+        long long token = proto.start(PsProto::PROCESSES);
         for (int i=0; i<(int)record.size(); i++) {
             if (!table.insertField(&proto, header[i], record[i])) {
                 fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
diff --git a/cmds/incident_helper/tests/CpuFreqParser_test.cpp b/cmds/incident_helper/tests/CpuFreqParser_test.cpp
index 82deee4..0839a7e 100644
--- a/cmds/incident_helper/tests/CpuFreqParser_test.cpp
+++ b/cmds/incident_helper/tests/CpuFreqParser_test.cpp
@@ -52,14 +52,14 @@
 TEST_F(CpuFreqParserTest, Success) {
     const string testFile = kTestDataPath + "cpufreq.txt";
     CpuFreqParser parser;
-    CpuFreq expected;
+    CpuFreqProto expected;
 
     long jiffyHz = sysconf(_SC_CLK_TCK);
     expected.set_jiffy_hz(jiffyHz);
 
-    CpuFreqStats::TimeInState* state;
+    CpuFreqProto::Stats::TimeInState* state;
 
-    CpuFreqStats* cpu0 = expected.add_cpu_freqs();
+    CpuFreqProto::Stats* cpu0 = expected.add_cpu_freqs();
     cpu0->set_cpu_name("cpu0");
     state = cpu0->add_times();
     state->set_state_khz(307200);
@@ -71,7 +71,7 @@
     state->set_state_khz(768000);
     state->set_time_jiffy(22652);
 
-    CpuFreqStats* cpu1 = expected.add_cpu_freqs();
+    CpuFreqProto::Stats* cpu1 = expected.add_cpu_freqs();
     cpu1->set_cpu_name("cpu1");
     state = cpu1->add_times();
     state->set_state_khz(307200);
@@ -83,7 +83,7 @@
     state->set_state_khz(768000);
     state->set_time_jiffy(22652);
 
-    CpuFreqStats* cpu2 = expected.add_cpu_freqs();
+    CpuFreqProto::Stats* cpu2 = expected.add_cpu_freqs();
     cpu2->set_cpu_name("cpu2");
     state = cpu2->add_times();
     state->set_state_khz(307200);
@@ -98,7 +98,7 @@
     state->set_state_khz(825600);
     state->set_time_jiffy(13173);
 
-    CpuFreqStats* cpu3 = expected.add_cpu_freqs();
+    CpuFreqProto::Stats* cpu3 = expected.add_cpu_freqs();
     cpu3->set_cpu_name("cpu3");
     state = cpu3->add_times();
     state->set_state_khz(307200);
diff --git a/cmds/incident_helper/tests/CpuInfoParser_test.cpp b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
index 8dce53e..2e50d63 100644
--- a/cmds/incident_helper/tests/CpuInfoParser_test.cpp
+++ b/cmds/incident_helper/tests/CpuInfoParser_test.cpp
@@ -52,28 +52,28 @@
 TEST_F(CpuInfoParserTest, Success) {
     const string testFile = kTestDataPath + "cpuinfo.txt";
     CpuInfoParser parser;
-    CpuInfo expected;
+    CpuInfoProto expected;
 
-    CpuInfo::TaskStats* taskStats = expected.mutable_task_stats();
+    CpuInfoProto::TaskStats* taskStats = expected.mutable_task_stats();
     taskStats->set_total(2038);
     taskStats->set_running(1);
     taskStats->set_sleeping(2033);
     taskStats->set_stopped(0);
     taskStats->set_zombie(0);
 
-    CpuInfo::MemStats* mem = expected.mutable_mem();
+    CpuInfoProto::MemStats* mem = expected.mutable_mem();
     mem->set_total(3842668);
     mem->set_used(3761936);
     mem->set_free(80732);
     mem->set_buffers(220188);
 
-    CpuInfo::MemStats* swap = expected.mutable_swap();
+    CpuInfoProto::MemStats* swap = expected.mutable_swap();
     swap->set_total(524284);
     swap->set_used(25892);
     swap->set_free(498392);
     swap->set_cached(1316952);
 
-    CpuInfo::CpuUsage* usage = expected.mutable_cpu_usage();
+    CpuInfoProto::CpuUsage* usage = expected.mutable_cpu_usage();
     usage->set_cpu(400);
     usage->set_user(17);
     usage->set_nice(0);
@@ -85,59 +85,59 @@
     usage->set_host(0);
 
     // This is a special line which is able to be parsed by the CpuInfoParser
-    CpuInfo::Task* task1 = expected.add_tasks();
+    CpuInfoProto::Task* task1 = expected.add_tasks();
     task1->set_pid(29438);
     task1->set_tid(29438);
     task1->set_user("rootabcdefghij");
     task1->set_pr("20");
     task1->set_ni(0);
     task1->set_cpu(57.9);
-    task1->set_s(CpuInfo::Task::STATUS_R);
+    task1->set_s(CpuInfoProto::Task::STATUS_R);
     task1->set_virt("14M");
     task1->set_res("3.8M");
-    task1->set_pcy(CpuInfo::Task::POLICY_UNKNOWN);
+    task1->set_pcy(CpuInfoProto::Task::POLICY_UNKNOWN);
     task1->set_cmd("top test");
     task1->set_name("top");
 
-    CpuInfo::Task* task2 = expected.add_tasks();
+    CpuInfoProto::Task* task2 = expected.add_tasks();
     task2->set_pid(916);
     task2->set_tid(916);
     task2->set_user("system");
     task2->set_pr("18");
     task2->set_ni(-2);
     task2->set_cpu(1.4);
-    task2->set_s(CpuInfo::Task::STATUS_S);
+    task2->set_s(CpuInfoProto::Task::STATUS_S);
     task2->set_virt("4.6G");
     task2->set_res("404M");
-    task2->set_pcy(CpuInfo::Task::POLICY_fg);
+    task2->set_pcy(CpuInfoProto::Task::POLICY_fg);
     task2->set_cmd("system_server");
     task2->set_name("system_server");
 
-    CpuInfo::Task* task3 = expected.add_tasks();
+    CpuInfoProto::Task* task3 = expected.add_tasks();
     task3->set_pid(28);
     task3->set_tid(28);
     task3->set_user("root");
     task3->set_pr("-2");
     task3->set_ni(0);
     task3->set_cpu(1.4);
-    task3->set_s(CpuInfo::Task::STATUS_S);
+    task3->set_s(CpuInfoProto::Task::STATUS_S);
     task3->set_virt("0");
     task3->set_res("0");
-    task3->set_pcy(CpuInfo::Task::POLICY_bg);
+    task3->set_pcy(CpuInfoProto::Task::POLICY_bg);
     task3->set_cmd("rcuc/3");
     task3->set_name("[rcuc/3]");
 
-    CpuInfo::Task* task4 = expected.add_tasks();
+    CpuInfoProto::Task* task4 = expected.add_tasks();
     task4->set_pid(27);
     task4->set_tid(27);
     task4->set_user("root");
     task4->set_pr("RT");
     task4->set_ni(0);
     task4->set_cpu(1.4);
-    task4->set_s(CpuInfo::Task::STATUS_S);
+    task4->set_s(CpuInfoProto::Task::STATUS_S);
     task4->set_virt("0");
     task4->set_res("0");
-    task4->set_pcy(CpuInfo::Task::POLICY_ta);
+    task4->set_pcy(CpuInfoProto::Task::POLICY_ta);
     task4->set_cmd("migration/3");
     task4->set_name("[migration/3]");
 
diff --git a/cmds/incident_helper/tests/KernelWakesParser_test.cpp b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
index a98c62b..f92d813 100644
--- a/cmds/incident_helper/tests/KernelWakesParser_test.cpp
+++ b/cmds/incident_helper/tests/KernelWakesParser_test.cpp
@@ -52,14 +52,14 @@
 TEST_F(KernelWakesParserTest, Short) {
     const string testFile = kTestDataPath + "kernel_wakeups_short.txt";
     KernelWakesParser parser;
-    KernelWakeSources expected;
+    KernelWakeSourcesProto expected;
 
-    WakeupSourceProto* record1 = expected.add_wakeup_sources();
+    KernelWakeSourcesProto::WakeupSource* record1 = expected.add_wakeup_sources();
     record1->set_name("ab");
     record1->set_active_count(8);
     record1->set_last_change(123456123456LL);
 
-    WakeupSourceProto* record2 = expected.add_wakeup_sources();
+    KernelWakeSourcesProto::WakeupSource* record2 = expected.add_wakeup_sources();
     record2->set_name("df");
     record2->set_active_count(143);
     record2->set_last_change(0LL);
@@ -76,9 +76,9 @@
 TEST_F(KernelWakesParserTest, Normal) {
     const string testFile = kTestDataPath + "kernel_wakeups.txt";
     KernelWakesParser parser;
-    KernelWakeSources expected;
+    KernelWakeSourcesProto expected;
 
-    WakeupSourceProto* record1 = expected.add_wakeup_sources();
+    KernelWakeSourcesProto::WakeupSource* record1 = expected.add_wakeup_sources();
     record1->set_name("ipc000000ab_ATFWD-daemon");
     record1->set_active_count(8);
     record1->set_event_count(8);
@@ -90,7 +90,7 @@
     record1->set_last_change(131348LL);
     record1->set_prevent_suspend_time(0LL);
 
-    WakeupSourceProto* record2 = expected.add_wakeup_sources();
+    KernelWakeSourcesProto::WakeupSource* record2 = expected.add_wakeup_sources();
     record2->set_name("ipc000000aa_ATFWD-daemon");
     record2->set_active_count(143);
     record2->set_event_count(143);
diff --git a/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
index a9e6e816..9bad7be 100644
--- a/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
+++ b/cmds/incident_helper/tests/PageTypeInfoParser_test.cpp
@@ -52,12 +52,12 @@
 TEST_F(PageTypeInfoParserTest, Success) {
     const string testFile = kTestDataPath + "pagetypeinfo.txt";
     PageTypeInfoParser parser;
-    PageTypeInfo expected;
+    PageTypeInfoProto expected;
 
     expected.set_page_block_order(10);
     expected.set_pages_per_block(1024);
 
-    MigrateTypeProto* mt1 = expected.add_migrate_types();
+    PageTypeInfoProto::MigrateType* mt1 = expected.add_migrate_types();
     mt1->set_node(0);
     mt1->set_zone("DMA");
     mt1->set_type("Unmovable");
@@ -66,7 +66,7 @@
         mt1->add_free_pages_count(arr1[i]);
     }
 
-    MigrateTypeProto* mt2 = expected.add_migrate_types();
+    PageTypeInfoProto::MigrateType* mt2 = expected.add_migrate_types();
     mt2->set_node(0);
     mt2->set_zone("Normal");
     mt2->set_type("Reclaimable");
@@ -75,7 +75,7 @@
         mt2->add_free_pages_count(arr2[i]);
     }
 
-    BlockProto* block1 = expected.add_blocks();
+    PageTypeInfoProto::Block* block1 = expected.add_blocks();
     block1->set_node(0);
     block1->set_zone("DMA");
     block1->set_unmovable(74);
@@ -86,7 +86,7 @@
     block1->set_isolate(0);
 
 
-    BlockProto* block2 = expected.add_blocks();
+    PageTypeInfoProto::Block* block2 = expected.add_blocks();
     block2->set_node(0);
     block2->set_zone("Normal");
     block2->set_unmovable(70);
diff --git a/cmds/incident_helper/tests/ProcrankParser_test.cpp b/cmds/incident_helper/tests/ProcrankParser_test.cpp
index 76b25d7..0b567ae 100644
--- a/cmds/incident_helper/tests/ProcrankParser_test.cpp
+++ b/cmds/incident_helper/tests/ProcrankParser_test.cpp
@@ -52,9 +52,9 @@
 TEST_F(ProcrankParserTest, HasSwapInfo) {
     const string testFile = kTestDataPath + "procrank.txt";
     ProcrankParser parser;
-    Procrank expected;
+    ProcrankProto expected;
 
-    ProcessProto* process1 = expected.add_processes();
+    ProcrankProto::Process* process1 = expected.add_processes();
     process1->set_pid(1119);
     process1->set_vss(2607640);
     process1->set_rss(339564);
@@ -66,7 +66,7 @@
     process1->set_zswap(10);
     process1->set_cmdline("system_server");
 
-    ProcessProto* process2 = expected.add_processes();
+    ProcrankProto::Process* process2 = expected.add_processes();
     process2->set_pid(649);
     process2->set_vss(11016);
     process2->set_rss(1448);
@@ -78,7 +78,7 @@
     process2->set_zswap(75);
     process2->set_cmdline("/vendor/bin/qseecomd");
 
-    ProcessProto* total = expected.mutable_summary()->mutable_total();
+    ProcrankProto::Process* total = expected.mutable_summary()->mutable_total();
     total->set_pss(1201993);
     total->set_uss(935300);
     total->set_swap(88164);
@@ -104,9 +104,9 @@
 TEST_F(ProcrankParserTest, NoSwapInfo) {
     const string testFile = kTestDataPath + "procrank_short.txt";
     ProcrankParser parser;
-    Procrank expected;
+    ProcrankProto expected;
 
-    ProcessProto* process1 = expected.add_processes();
+    ProcrankProto::Process* process1 = expected.add_processes();
     process1->set_pid(1119);
     process1->set_vss(2607640);
     process1->set_rss(339564);
@@ -114,7 +114,7 @@
     process1->set_uss(114216);
     process1->set_cmdline("system_server");
 
-    ProcessProto* process2 = expected.add_processes();
+    ProcrankProto::Process* process2 = expected.add_processes();
     process2->set_pid(649);
     process2->set_vss(11016);
     process2->set_rss(1448);
@@ -122,7 +122,7 @@
     process2->set_uss(48);
     process2->set_cmdline("/vendor/bin/qseecomd");
 
-    ProcessProto* total = expected.mutable_summary()->mutable_total();
+    ProcrankProto::Process* total = expected.mutable_summary()->mutable_total();
     total->set_pss(1201993);
     total->set_uss(935300);
     total->set_cmdline("TOTAL");
diff --git a/cmds/incident_helper/tests/PsParser_test.cpp b/cmds/incident_helper/tests/PsParser_test.cpp
index 1f03a7f..114d634 100644
--- a/cmds/incident_helper/tests/PsParser_test.cpp
+++ b/cmds/incident_helper/tests/PsParser_test.cpp
@@ -52,10 +52,10 @@
 TEST_F(PsParserTest, Normal) {
     const string testFile = kTestDataPath + "ps.txt";
     PsParser parser;
-    PsDumpProto expected;
-    PsDumpProto got;
+    PsProto expected;
+    PsProto got;
 
-    PsDumpProto::Process* record1 = expected.add_processes();
+    PsProto::Process* record1 = expected.add_processes();
     record1->set_label("u:r:init:s0");
     record1->set_user("root");
     record1->set_pid(1);
@@ -65,16 +65,16 @@
     record1->set_rss(2636);
     record1->set_wchan("SyS_epoll_wait");
     record1->set_addr("0");
-    record1->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S);
+    record1->set_s(PsProto_Process_ProcessStateCode_STATE_S);
     record1->set_pri(19);
     record1->set_ni(0);
     record1->set_rtprio("-");
-    record1->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL);
-    record1->set_pcy(PsDumpProto::Process::POLICY_FG);
+    record1->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL);
+    record1->set_pcy(PsProto::Process::POLICY_FG);
     record1->set_time("00:00:01");
     record1->set_cmd("init");
 
-    PsDumpProto::Process* record2 = expected.add_processes();
+    PsProto::Process* record2 = expected.add_processes();
     record2->set_label("u:r:kernel:s0");
     record2->set_user("root");
     record2->set_pid(2);
@@ -84,16 +84,16 @@
     record2->set_rss(0);
     record2->set_wchan("kthreadd");
     record2->set_addr("0");
-    record2->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S);
+    record2->set_s(PsProto_Process_ProcessStateCode_STATE_S);
     record2->set_pri(19);
     record2->set_ni(0);
     record2->set_rtprio("-");
-    record2->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL);
-    record2->set_pcy(PsDumpProto::Process::POLICY_FG);
+    record2->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL);
+    record2->set_pcy(PsProto::Process::POLICY_FG);
     record2->set_time("00:00:00");
     record2->set_cmd("kthreadd");
 
-    PsDumpProto::Process* record3 = expected.add_processes();
+    PsProto::Process* record3 = expected.add_processes();
     record3->set_label("u:r:surfaceflinger:s0");
     record3->set_user("system");
     record3->set_pid(499);
@@ -103,16 +103,16 @@
     record3->set_rss(22024);
     record3->set_wchan("futex_wait_queue_me");
     record3->set_addr("0");
-    record3->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S);
+    record3->set_s(PsProto_Process_ProcessStateCode_STATE_S);
     record3->set_pri(42);
     record3->set_ni(-9);
     record3->set_rtprio("2");
-    record3->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_FIFO);
-    record3->set_pcy(PsDumpProto::Process::POLICY_FG);
+    record3->set_sch(PsProto_Process_SchedulingPolicy_SCH_FIFO);
+    record3->set_pcy(PsProto::Process::POLICY_FG);
     record3->set_time("00:00:00");
     record3->set_cmd("EventThread");
 
-    PsDumpProto::Process* record4 = expected.add_processes();
+    PsProto::Process* record4 = expected.add_processes();
     record4->set_label("u:r:hal_gnss_default:s0");
     record4->set_user("gps");
     record4->set_pid(670);
@@ -122,16 +122,16 @@
     record4->set_rss(7272);
     record4->set_wchan("poll_schedule_timeout");
     record4->set_addr("0");
-    record4->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S);
+    record4->set_s(PsProto_Process_ProcessStateCode_STATE_S);
     record4->set_pri(19);
     record4->set_ni(0);
     record4->set_rtprio("-");
-    record4->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL);
-    record4->set_pcy(PsDumpProto::Process::POLICY_FG);
+    record4->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL);
+    record4->set_pcy(PsProto::Process::POLICY_FG);
     record4->set_time("00:00:00");
     record4->set_cmd("Loc_hal_worker");
 
-    PsDumpProto::Process* record5 = expected.add_processes();
+    PsProto::Process* record5 = expected.add_processes();
     record5->set_label("u:r:platform_app:s0:c512,c768");
     record5->set_user("u0_a48");
     record5->set_pid(1660);
@@ -141,16 +141,16 @@
     record5->set_rss(138328);
     record5->set_wchan("binder_thread_read");
     record5->set_addr("0");
-    record5->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S);
+    record5->set_s(PsProto_Process_ProcessStateCode_STATE_S);
     record5->set_pri(35);
     record5->set_ni(-16);
     record5->set_rtprio("-");
-    record5->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL);
-    record5->set_pcy(PsDumpProto::Process::POLICY_TA);
+    record5->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL);
+    record5->set_pcy(PsProto::Process::POLICY_TA);
     record5->set_time("00:00:00");
     record5->set_cmd("HwBinder:1660_1");
 
-    PsDumpProto::Process* record6 = expected.add_processes();
+    PsProto::Process* record6 = expected.add_processes();
     record6->set_label("u:r:perfd:s0");
     record6->set_user("root");
     record6->set_pid(1939);
@@ -160,16 +160,16 @@
     record6->set_rss(2088);
     record6->set_wchan("__skb_recv_datagram");
     record6->set_addr("7b9782fd14");
-    record6->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S);
+    record6->set_s(PsProto_Process_ProcessStateCode_STATE_S);
     record6->set_pri(19);
     record6->set_ni(0);
     record6->set_rtprio("-");
-    record6->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL);
-    record6->set_pcy(PsDumpProto::Process::POLICY_UNKNOWN);
+    record6->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL);
+    record6->set_pcy(PsProto::Process::POLICY_UNKNOWN);
     record6->set_time("00:00:00");
     record6->set_cmd("perfd");
 
-    PsDumpProto::Process* record7 = expected.add_processes();
+    PsProto::Process* record7 = expected.add_processes();
     record7->set_label("u:r:perfd:s0");
     record7->set_user("root");
     record7->set_pid(1939);
@@ -179,16 +179,16 @@
     record7->set_rss(2088);
     record7->set_wchan("do_sigtimedwait");
     record7->set_addr("7b9782ff6c");
-    record7->set_s(PsDumpProto_Process_ProcessStateCode_STATE_S);
+    record7->set_s(PsProto_Process_ProcessStateCode_STATE_S);
     record7->set_pri(19);
     record7->set_ni(0);
     record7->set_rtprio("-");
-    record7->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL);
-    record7->set_pcy(PsDumpProto::Process::POLICY_UNKNOWN);
+    record7->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL);
+    record7->set_pcy(PsProto::Process::POLICY_UNKNOWN);
     record7->set_time("00:00:00");
     record7->set_cmd("POSIX timer 0");
 
-    PsDumpProto::Process* record8 = expected.add_processes();
+    PsProto::Process* record8 = expected.add_processes();
     record8->set_label("u:r:shell:s0");
     record8->set_user("shell");
     record8->set_pid(2645);
@@ -198,12 +198,12 @@
     record8->set_rss(2972);
     record8->set_wchan("0");
     record8->set_addr("7f67a2f8b4");
-    record8->set_s(PsDumpProto_Process_ProcessStateCode_STATE_R);
+    record8->set_s(PsProto_Process_ProcessStateCode_STATE_R);
     record8->set_pri(19);
     record8->set_ni(0);
     record8->set_rtprio("-");
-    record8->set_sch(PsDumpProto_Process_SchedulingPolicy_SCH_NORMAL);
-    record8->set_pcy(PsDumpProto::Process::POLICY_FG);
+    record8->set_sch(PsProto_Process_SchedulingPolicy_SCH_NORMAL);
+    record8->set_pcy(PsProto::Process::POLICY_FG);
     record8->set_time("00:00:00");
     record8->set_cmd("ps");
 
@@ -221,8 +221,8 @@
     } else {
         int n = got.processes_size();
         for (int i = 0; i < n; i++) {
-            PsDumpProto::Process g = got.processes(i);
-            PsDumpProto::Process e = expected.processes(i);
+            PsProto::Process g = got.processes(i);
+            PsProto::Process e = expected.processes(i);
 
             if (g.label() != e.label()) {
                 fprintf(stderr, "prcs[%d]: Invalid label. Got %s, want %s\n", i, g.label().c_str(), e.label().c_str());
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index f254327..7baa5e5 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -42,6 +42,7 @@
     const static int kDimensionKeySizeHardLimit = 500;
 
     const static int kMaxConfigCount = 10;
+    const static int kMaxAlertCountPerConfig = 100;
     const static int kMaxConditionCountPerConfig = 200;
     const static int kMaxMetricCountPerConfig = 300;
     const static int kMaxMatcherCountPerConfig = 500;
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 178db1a..af2e362 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -63,7 +63,8 @@
     : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) {
     // TODO: evaluate initial conditions. and set mConditionMet.
     if (metric.has_bucket()) {
-        mBucketSizeNs = TimeUnitToBucketSizeInMillis(metric.bucket()) * 1000000;
+        mBucketSizeNs =
+                TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
     } else {
         mBucketSizeNs = LLONG_MAX;
     }
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 67d95db..3b7936d 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -72,7 +72,8 @@
     // them in the base class, because the proto generated CountMetric, and DurationMetric are
     // not related. Maybe we should add a template in the future??
     if (metric.has_bucket()) {
-        mBucketSizeNs = TimeUnitToBucketSizeInMillis(metric.bucket()) * 1000000;
+        mBucketSizeNs =
+                TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
     } else {
         mBucketSizeNs = LLONG_MAX;
     }
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 8aa8169..0daa506 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -69,7 +69,7 @@
     mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
     int64_t bucketSizeMills = 0;
     if (metric.has_bucket()) {
-        bucketSizeMills = TimeUnitToBucketSizeInMillis(metric.bucket());
+        bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
     } else {
         bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR);
     }
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index e8f8299..8663e5e 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -123,6 +123,7 @@
         return byteSizeLocked();
     }
 
+    /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
     virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert) {
         std::lock_guard<std::mutex> lock(mMutex);
         sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey);
@@ -137,6 +138,11 @@
         return mBucketSizeNs;
     }
 
+    // Only needed for unit-testing to override guardrail.
+    void setBucketSize(int64_t bucketSize) {
+        mBucketSizeNs = bucketSize;
+    }
+
     inline const int64_t& getMetricId() {
         return mMetricId;
     }
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 13e1f8c..e75b710 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -92,8 +92,10 @@
         ALOGE("This config is too big! Reject!");
         mConfigValid = false;
     }
-
-    // TODO: add alert size.
+    if (mAllAnomalyTrackers.size() > StatsdStats::kMaxAlertCountPerConfig) {
+        ALOGE("This config has too many alerts! Reject!");
+        mConfigValid = false;
+    }
     // no matter whether this config is valid, log it in the stats.
     StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(),
                                                   mAllConditionTrackers.size(),
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index cbca884..35fcdc4 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -72,7 +72,7 @@
     // TODO: valuemetric for pushed events may need unlimited bucket length
     int64_t bucketSizeMills = 0;
     if (metric.has_bucket()) {
-        bucketSizeMills = TimeUnitToBucketSizeInMillis(metric.bucket());
+        bucketSizeMills = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket());
     } else {
         bucketSizeMills = TimeUnitToBucketSizeInMillis(ONE_HOUR);
     }
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 769f46d..71e5c33 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -513,10 +513,12 @@
         const int metricIndex = itr->second;
         sp<MetricProducer> metric = allMetricProducers[metricIndex];
         sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert);
-        if (anomalyTracker != nullptr) {
-            anomalyTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
-            allAnomalyTrackers.push_back(anomalyTracker);
+        if (anomalyTracker == nullptr) {
+            // The ALOGW for this invalid alert was already displayed in addAnomalyTracker().
+            return false;
         }
+        anomalyTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
+        allAnomalyTrackers.push_back(anomalyTracker);
     }
     for (int i = 0; i < config.subscription_size(); ++i) {
         const Subscription& subscription = config.subscription(i);
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index f7b768f..30eef4f 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -17,6 +17,7 @@
 #include "stats_log_util.h"
 
 #include <logd/LogEvent.h>
+#include <private/android_filesystem_config.h>
 #include <utils/Log.h>
 #include <set>
 #include <stack>
@@ -216,6 +217,14 @@
     protoOutput->end(atomToken);
 }
 
+int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit) {
+    int64_t bucketSizeMillis = TimeUnitToBucketSizeInMillis(unit);
+    if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL) {
+        bucketSizeMillis = 5 * 60 * 1000LL;
+    }
+    return bucketSizeMillis;
+}
+
 int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) {
     switch (unit) {
         case ONE_MINUTE:
@@ -283,4 +292,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
index 32fe0b8..6a5123d 100644
--- a/cmds/statsd/src/stats_log_util.h
+++ b/cmds/statsd/src/stats_log_util.h
@@ -32,6 +32,10 @@
 void writeDimensionToProto(const HashableDimensionKey& dimension,
                            util::ProtoOutputStream* protoOutput);
 
+// Convert the TimeUnit enum to the bucket size in millis with a guardrail on
+// bucket size.
+int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit);
+
 // Convert the TimeUnit enum to the bucket size in millis.
 int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit);
 
@@ -71,4 +75,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index 93cd587..0228004 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -47,7 +47,7 @@
     *countMetric->mutable_dimensions_in_what() =
         CreateAttributionUidAndTagDimensions(
             android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
-    countMetric->set_bucket(ONE_MINUTE);
+    countMetric->set_bucket(FIVE_MINUTES);
     return config;
 }
 
@@ -206,4 +206,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
index 293f579..4dffd13 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_test.cpp
@@ -61,7 +61,7 @@
             CreateDimensions(android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */});
     *metric->mutable_dimensions_in_condition() = CreateAttributionUidDimensions(
             android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
-    metric->set_bucket(ONE_MINUTE);
+    metric->set_bucket(FIVE_MINUTES);
     return config;
 }
 
@@ -252,7 +252,7 @@
     addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
 
     auto metric = config.add_count_metric();
-    metric->set_bucket(ONE_MINUTE);
+    metric->set_bucket(FIVE_MINUTES);
     metric->set_id(StringToId("AppCrashMetric"));
     metric->set_what(appCrashMatcher.id());
     metric->set_condition(combinationPredicate->id());
@@ -441,7 +441,7 @@
     addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
 
     auto metric = config.add_duration_metric();
-    metric->set_bucket(ONE_MINUTE);
+    metric->set_bucket(FIVE_MINUTES);
     metric->set_id(StringToId("BatterySaverModeDurationMetric"));
     metric->set_what(inBatterySaverModePredicate.id());
     metric->set_condition(combinationPredicate->id());
@@ -595,7 +595,7 @@
     addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
 
     auto metric = config.add_duration_metric();
-    metric->set_bucket(ONE_MINUTE);
+    metric->set_bucket(FIVE_MINUTES);
     metric->set_id(StringToId("AppInBackgroundMetric"));
     metric->set_what(isInBackgroundPredicate.id());
     metric->set_condition(combinationPredicate->id());
@@ -730,4 +730,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
index 6aa7dd6..3843e0a 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_test.cpp
@@ -53,7 +53,7 @@
     fieldMatcher->add_child()->set_field(7);  // activity_start_msec(int64)
     *gaugeMetric->mutable_dimensions_in_what() =
         CreateDimensions(android::util::APP_START_CHANGED, {1 /* uid field */ });
-    gaugeMetric->set_bucket(ONE_MINUTE);
+    gaugeMetric->set_bucket(FIVE_MINUTES);
 
     auto links = gaugeMetric->add_links();
     links->set_condition(isInBackgroundPredicate.id());
@@ -198,4 +198,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index d005181..1b51780 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -71,7 +71,7 @@
     // The metric is dimensioning by uid only.
     *countMetric->mutable_dimensions_in_what() =
         CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1});
-    countMetric->set_bucket(ONE_MINUTE);
+    countMetric->set_bucket(FIVE_MINUTES);
 
     // Links between crash atom and condition of app is in syncing.
     auto links = countMetric->add_links();
@@ -337,4 +337,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index 024fa3e..efdab98 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -56,7 +56,7 @@
     *durationMetric->mutable_dimensions_in_what() =
         CreateAttributionUidDimensions(
             android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
-    durationMetric->set_bucket(ONE_MINUTE);
+    durationMetric->set_bucket(FIVE_MINUTES);
     return config;
 }
 
@@ -327,4 +327,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index d9dbf1d..20ddbe9 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -56,6 +56,7 @@
 
     CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       bucketStartTimeNs);
+    countProducer.setBucketSize(60 * NS_PER_SEC);
 
     // 2 events in bucket 1.
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
@@ -118,6 +119,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
+    countProducer.setBucketSize(60 * NS_PER_SEC);
 
     countProducer.onConditionChanged(true, bucketStartTimeNs);
     countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
@@ -179,6 +181,7 @@
 
     CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
                                       bucketStartTimeNs);
+    countProducer.setBucketSize(60 * NS_PER_SEC);
 
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
     countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
@@ -217,6 +220,8 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
                                       bucketStartTimeNs);
+    countProducer.setBucketSize(60 * NS_PER_SEC);
+
     sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert);
     EXPECT_TRUE(anomalyTracker != nullptr);
 
@@ -274,6 +279,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
                                       bucketStartTimeNs);
+    countProducer.setBucketSize(60 * NS_PER_SEC);
 
     // Bucket is flushed yet.
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
@@ -329,6 +335,8 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       bucketStartTimeNs);
+    countProducer.setBucketSize(60 * NS_PER_SEC);
+
     sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert);
 
     int tagId = 1;
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 57e2794..7969596 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -59,6 +59,7 @@
     DurationMetricProducer durationProducer(
             kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */,
             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+    durationProducer.setBucketSize(60 * NS_PER_SEC);
 
     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
@@ -100,6 +101,8 @@
     DurationMetricProducer durationProducer(
             kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+    durationProducer.setBucketSize(60 * NS_PER_SEC);
+
     EXPECT_FALSE(durationProducer.mCondition);
     EXPECT_FALSE(durationProducer.isConditionSliced());
 
@@ -149,6 +152,7 @@
     DurationMetricProducer durationProducer(
             kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+    durationProducer.setBucketSize(60 * NS_PER_SEC);
 
     LogEvent start_event(tagId, startTimeNs);
     start_event.init();
@@ -203,6 +207,7 @@
     DurationMetricProducer durationProducer(
             kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+    durationProducer.setBucketSize(60 * NS_PER_SEC);
 
     LogEvent start_event(tagId, startTimeNs);
     start_event.init();
@@ -256,6 +261,8 @@
     DurationMetricProducer durationProducer(
             kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+    durationProducer.setBucketSize(60 * NS_PER_SEC);
+
     sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert);
     EXPECT_TRUE(anomalyTracker != nullptr);
 
@@ -293,6 +300,7 @@
     DurationMetricProducer durationProducer(
             kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+    durationProducer.setBucketSize(60 * NS_PER_SEC);
 
     LogEvent start_event(tagId, startTimeNs);
     start_event.init();
@@ -340,6 +348,7 @@
     DurationMetricProducer durationProducer(
             kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
+    durationProducer.setBucketSize(60 * NS_PER_SEC);
 
     LogEvent start_event(tagId, startTimeNs);
     start_event.init();
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 8b4273b..0eb8ce2 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -67,6 +67,7 @@
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       tagId, bucketStartTimeNs, pullerManager);
+    gaugeProducer.setBucketSize(60 * NS_PER_SEC);
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -144,6 +145,7 @@
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       -1 /* -1 means no pulling */, bucketStartTimeNs,
                                       pullerManager);
+    gaugeProducer.setBucketSize(60 * NS_PER_SEC);
     sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert);
     EXPECT_TRUE(anomalyTracker != nullptr);
 
@@ -225,6 +227,7 @@
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       tagId, bucketStartTimeNs, pullerManager);
+    gaugeProducer.setBucketSize(60 * NS_PER_SEC);
 
     vector<shared_ptr<LogEvent>> allData;
     shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
@@ -292,6 +295,7 @@
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId,
                                       bucketStartTimeNs, pullerManager);
+    gaugeProducer.setBucketSize(60 * NS_PER_SEC);
 
     gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
@@ -350,6 +354,7 @@
     gaugeFieldMatcher->add_child()->set_field(2);
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       tagId, bucketStartTimeNs, pullerManager);
+    gaugeProducer.setBucketSize(60 * NS_PER_SEC);
 
     Alert alert;
     alert.set_id(101);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 6e66c6e..ce4fa32 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -66,6 +66,7 @@
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       tagId, bucketStartTimeNs, pullerManager);
+    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -79,6 +80,8 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    valueProducer.setBucketSize(60 * NS_PER_SEC);
+
     // startUpdated:true tainted:0 sum:0 start:11
     EXPECT_EQ(true, curInterval.startUpdated);
     EXPECT_EQ(0, curInterval.tainted);
@@ -162,7 +165,7 @@
 
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
                                       pullerManager);
-
+    valueProducer.setBucketSize(60 * NS_PER_SEC);
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
 
     // has one slice
@@ -215,6 +218,7 @@
             make_shared<StrictMock<MockStatsPullerManager>>();
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
                                       pullerManager);
+    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -269,6 +273,7 @@
             }));
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, tagId, bucketStartTimeNs,
                                       pullerManager);
+    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -311,6 +316,7 @@
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
                                       pullerManager);
+    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);
@@ -357,6 +363,8 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       -1 /*not pulled*/, bucketStartTimeNs);
+    valueProducer.setBucketSize(60 * NS_PER_SEC);
+
     sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert);
 
 
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 3283759..6c3d248 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7162,8 +7162,8 @@
 
                 String appName = getApplicationInfo().loadLabel(getPackageManager())
                         .toString();
-                String warning = "Detected problems with API compatiblity\n"
-                                 + "(please consult log for detail)";
+                String warning = "Detected problems with API compatibility\n"
+                                 + "(visit g.co/dev/appcompat for more info)";
                 if (isAppDebuggable) {
                     new AlertDialog.Builder(this)
                         .setTitle(appName)
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index ae47a68..03faeee 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -44,6 +44,7 @@
 import android.graphics.Matrix;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.net.Uri;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Build;
@@ -2750,6 +2751,30 @@
     }
 
     /**
+     * Updates (grants or revokes) a persitable URI permission.
+     *
+     * @param uri URI to be granted or revoked.
+     * @param prefix if {@code false}, permission apply to this specific URI; if {@code true}, it
+     * applies to all URIs that are prefixed by this URI.
+     * @param packageName target package.
+     * @param grant if {@code true} a new permission will be granted, otherwise an existing
+     * permission will be revoked.
+     *
+     * @return whether or not the requested succeeded.
+     *
+     * @hide
+     */
+    public boolean updatePersistableUriPermission(Uri uri, boolean prefix, String packageName,
+            boolean grant) {
+        try {
+            return getService().updatePersistableUriPermission(uri, prefix, packageName, grant,
+                    UserHandle.myUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Information you can retrieve about any processes that are in an error condition.
      */
     public static class ProcessErrorStateInfo implements Parcelable {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index fee5827..d5430f0 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -1106,6 +1106,11 @@
     }
 
     /** @hide */
+    public void setRemoteAnimationAdapter(RemoteAnimationAdapter remoteAnimationAdapter) {
+        mRemoteAnimationAdapter = remoteAnimationAdapter;
+    }
+
+    /** @hide */
     public static ActivityOptions fromBundle(Bundle bOptions) {
         return bOptions != null ? new ActivityOptions(bOptions) : null;
     }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 02be002..f6e5f37 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -68,6 +68,7 @@
 import android.service.voice.IVoiceInteractionSession;
 import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationDefinition;
+import android.view.RemoteAnimationAdapter;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IKeyguardDismissCallback;
@@ -423,6 +424,8 @@
     void restart();
     void performIdleMaintenance();
     void takePersistableUriPermission(in Uri uri, int modeFlags, int userId);
+    boolean updatePersistableUriPermission(in Uri uri, boolean prefix, String packageName,
+                                           boolean grant, int userId);
     void releasePersistableUriPermission(in Uri uri, int modeFlags, int userId);
     ParceledListSlice getPersistedUriPermissions(in String packageName, boolean incoming);
     void appNotRespondingViaProvider(in IBinder connection);
@@ -696,4 +699,11 @@
       * Registers remote animations for a specific activity.
       */
      void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition);
+
+     /**
+      * Registers a remote animation to be run for all activity starts from a certain package during
+      * a short predefined amount of time.
+      */
+     void registerRemoteAnimationForNextActivityStart(in String packageName,
+            in RemoteAnimationAdapter adapter);
 }
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 69ec26c..faaa004 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -319,6 +319,7 @@
      * {@link SecurityEvent#getData()}:
      * <li> [0] admin package name ({@code String}),
      * <li> [1] admin user ID ({@code Integer}).
+     * <li> [2] target user ID ({@code Integer})
      */
     public static final int TAG_REMOTE_LOCK = SecurityLogTags.SECURITY_REMOTE_LOCK;
 
diff --git a/core/java/android/app/backup/OWNERS b/core/java/android/app/backup/OWNERS
new file mode 100644
index 0000000..1c9a43a
--- /dev/null
+++ b/core/java/android/app/backup/OWNERS
@@ -0,0 +1,7 @@
+artikz@google.com
+brufino@google.com
+bryanmawhinney@google.com
+ctate@google.com
+jorlow@google.com
+mkarpinski@google.com
+
diff --git a/core/java/android/app/servertransaction/ActivityLifecycleItem.java b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
index 9a50a00..7f8c50c 100644
--- a/core/java/android/app/servertransaction/ActivityLifecycleItem.java
+++ b/core/java/android/app/servertransaction/ActivityLifecycleItem.java
@@ -91,4 +91,9 @@
         pw.println(prefix + "target state:" + getTargetState());
         pw.println(prefix + "description: " + mDescription);
     }
+
+    @Override
+    public void recycle() {
+        setDescription(null);
+    }
 }
diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java
index 48a79f7..0edcf18 100644
--- a/core/java/android/app/servertransaction/DestroyActivityItem.java
+++ b/core/java/android/app/servertransaction/DestroyActivityItem.java
@@ -65,6 +65,7 @@
 
     @Override
     public void recycle() {
+        super.recycle();
         mFinished = false;
         mConfigChanges = 0;
         ObjectPool.recycle(this);
diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java
index 70a4755..91e73cd 100644
--- a/core/java/android/app/servertransaction/PauseActivityItem.java
+++ b/core/java/android/app/servertransaction/PauseActivityItem.java
@@ -102,6 +102,7 @@
 
     @Override
     public void recycle() {
+        super.recycle();
         mFinished = false;
         mUserLeaving = false;
         mConfigChanges = 0;
diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java
index ed90f2c..af2fb71 100644
--- a/core/java/android/app/servertransaction/ResumeActivityItem.java
+++ b/core/java/android/app/servertransaction/ResumeActivityItem.java
@@ -101,6 +101,7 @@
 
     @Override
     public void recycle() {
+        super.recycle();
         mProcState = ActivityManager.PROCESS_STATE_UNKNOWN;
         mUpdateProcState = false;
         mIsForward = false;
diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java
index b814d1a..f955a90 100644
--- a/core/java/android/app/servertransaction/StopActivityItem.java
+++ b/core/java/android/app/servertransaction/StopActivityItem.java
@@ -72,6 +72,7 @@
 
     @Override
     public void recycle() {
+        super.recycle();
         mShowWindow = false;
         mConfigChanges = 0;
         ObjectPool.recycle(this);
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index bc7823b..1dc7549 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2307,6 +2307,9 @@
         } else if (profile == BluetoothProfile.HID_DEVICE) {
             BluetoothHidDevice hidDevice = new BluetoothHidDevice(context, listener);
             return true;
+        } else if (profile == BluetoothProfile.HEARING_AID) {
+            BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener);
+            return true;
         } else {
             return false;
         }
@@ -2389,6 +2392,9 @@
                 BluetoothHidDevice hidDevice = (BluetoothHidDevice) proxy;
                 hidDevice.close();
                 break;
+            case BluetoothProfile.HEARING_AID:
+                BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy;
+                hearingAid.close();
         }
     }
 
diff --git a/core/java/android/bluetooth/BluetoothHearingAid.java b/core/java/android/bluetooth/BluetoothHearingAid.java
new file mode 100644
index 0000000..647e0d0
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothHearingAid.java
@@ -0,0 +1,693 @@
+/*
+ * 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.bluetooth;
+
+import android.Manifest;
+import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * This class provides the public APIs to control the Bluetooth Hearing Aid
+ * profile.
+ *
+ * <p>BluetoothHearingAid is a proxy object for controlling the Bluetooth Hearing Aid
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothHearingAid proxy object.
+ *
+ * <p> Each method is protected with its appropriate permission.
+ * @hide
+ */
+public final class BluetoothHearingAid implements BluetoothProfile {
+    private static final String TAG = "BluetoothHearingAid";
+    private static final boolean DBG = false;
+    private static final boolean VDBG = false;
+
+    /**
+     * Intent used to broadcast the change in connection state of the Hearing Aid
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+     * </ul>
+     *
+     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+            "android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED";
+
+    /**
+     * Intent used to broadcast the change in the Playing state of the Hearing Aid
+     * profile.
+     *
+     * <p>This intent will have 3 extras:
+     * <ul>
+     * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+     * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile. </li>
+     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+     * </ul>
+     *
+     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+     * {@link #STATE_PLAYING}, {@link #STATE_NOT_PLAYING},
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PLAYING_STATE_CHANGED =
+            "android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED";
+
+    /**
+     * Intent used to broadcast the selection of a connected device as active.
+     *
+     * <p>This intent will have one extra:
+     * <ul>
+     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
+     * be null if no device is active. </li>
+     * </ul>
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+     * receive.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_ACTIVE_DEVICE_CHANGED =
+            "android.bluetooth.hearingaid.profile.action.ACTIVE_DEVICE_CHANGED";
+
+    /**
+     * Hearing Aid device is streaming music. This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+     */
+    public static final int STATE_PLAYING = 10;
+
+    /**
+     * Hearing Aid device is NOT streaming music. This state can be one of
+     * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
+     * {@link #ACTION_PLAYING_STATE_CHANGED} intent.
+     */
+    public static final int STATE_NOT_PLAYING = 11;
+
+    /** This device represents Left Hearing Aid. */
+    public static final int SIDE_LEFT = IBluetoothHearingAid.SIDE_LEFT;
+
+    /** This device represents Right Hearing Aid. */
+    public static final int SIDE_RIGHT = IBluetoothHearingAid.SIDE_RIGHT;
+
+    /** This device is Monaural. */
+    public static final int MODE_MONAURAL = IBluetoothHearingAid.MODE_MONAURAL;
+
+    /** This device is Binaural (should receive only left or right audio). */
+    public static final int MODE_BINAURAL = IBluetoothHearingAid.MODE_BINAURAL;
+
+    /** Can't read ClientID for this device */
+    public static final long HI_SYNC_ID_INVALID = IBluetoothHearingAid.HI_SYNC_ID_INVALID;
+
+    private Context mContext;
+    private ServiceListener mServiceListener;
+    private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
+    @GuardedBy("mServiceLock")
+    private IBluetoothHearingAid mService;
+    private BluetoothAdapter mAdapter;
+
+    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+            new IBluetoothStateChangeCallback.Stub() {
+                public void onBluetoothStateChange(boolean up) {
+                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
+                    if (!up) {
+                        if (VDBG) Log.d(TAG, "Unbinding service...");
+                        try {
+                            mServiceLock.writeLock().lock();
+                            mService = null;
+                            mContext.unbindService(mConnection);
+                        } catch (Exception re) {
+                            Log.e(TAG, "", re);
+                        } finally {
+                            mServiceLock.writeLock().unlock();
+                        }
+                    } else {
+                        try {
+                            mServiceLock.readLock().lock();
+                            if (mService == null) {
+                                if (VDBG) Log.d(TAG, "Binding service...");
+                                doBind();
+                            }
+                        } catch (Exception re) {
+                            Log.e(TAG, "", re);
+                        } finally {
+                            mServiceLock.readLock().unlock();
+                        }
+                    }
+                }
+            };
+
+    /**
+     * Create a BluetoothHearingAid proxy object for interacting with the local
+     * Bluetooth Hearing Aid service.
+     */
+    /*package*/ BluetoothHearingAid(Context context, ServiceListener l) {
+        mContext = context;
+        mServiceListener = l;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+        }
+
+        doBind();
+    }
+
+    void doBind() {
+        Intent intent = new Intent(IBluetoothHearingAid.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+                android.os.Process.myUserHandle())) {
+            Log.e(TAG, "Could not bind to Bluetooth Hearing Aid Service with " + intent);
+            return;
+        }
+    }
+
+    /*package*/ void close() {
+        mServiceListener = null;
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (Exception e) {
+                Log.e(TAG, "", e);
+            }
+        }
+
+        try {
+            mServiceLock.writeLock().lock();
+            if (mService != null) {
+                mService = null;
+                mContext.unbindService(mConnection);
+            }
+        } catch (Exception re) {
+            Log.e(TAG, "", re);
+        } finally {
+            mServiceLock.writeLock().unlock();
+        }
+    }
+
+    @Override
+    public void finalize() {
+        // The empty finalize needs to be kept or the
+        // cts signature tests would fail.
+    }
+
+    /**
+     * Initiate connection to a profile of the remote bluetooth device.
+     *
+     * <p> This API returns false in scenarios like the profile on the
+     * device is already connected or Bluetooth is not turned on.
+     * When this API returns true, it is guaranteed that
+     * connection state intent for the profile will be broadcasted with
+     * the state. Users can get the connection state of the profile
+     * from this intent.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     * permission.
+     *
+     * @param device Remote Bluetooth Device
+     * @return false on immediate error, true otherwise
+     * @hide
+     */
+    public boolean connect(BluetoothDevice device) {
+        if (DBG) log("connect(" + device + ")");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled() && isValidDevice(device)) {
+                return mService.connect(device);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Initiate disconnection from a profile
+     *
+     * <p> This API will return false in scenarios like the profile on the
+     * Bluetooth device is not in connected state etc. When this API returns,
+     * true, it is guaranteed that the connection state change
+     * intent will be broadcasted with the state. Users can get the
+     * disconnection state of the profile from this intent.
+     *
+     * <p> If the disconnection is initiated by a remote device, the state
+     * will transition from {@link #STATE_CONNECTED} to
+     * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
+     * host (local) device the state will transition from
+     * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
+     * state {@link #STATE_DISCONNECTED}. The transition to
+     * {@link #STATE_DISCONNECTING} can be used to distinguish between the
+     * two scenarios.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     * permission.
+     *
+     * @param device Remote Bluetooth Device
+     * @return false on immediate error, true otherwise
+     * @hide
+     */
+    public boolean disconnect(BluetoothDevice device) {
+        if (DBG) log("disconnect(" + device + ")");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled() && isValidDevice(device)) {
+                return mService.disconnect(device);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<BluetoothDevice> getConnectedDevices() {
+        if (VDBG) log("getConnectedDevices()");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()) {
+                return mService.getConnectedDevices();
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return new ArrayList<BluetoothDevice>();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return new ArrayList<BluetoothDevice>();
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        if (VDBG) log("getDevicesMatchingStates()");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()) {
+                return mService.getDevicesMatchingConnectionStates(states);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return new ArrayList<BluetoothDevice>();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return new ArrayList<BluetoothDevice>();
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getConnectionState(BluetoothDevice device) {
+        if (VDBG) log("getState(" + device + ")");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && isValidDevice(device)) {
+                return mService.getConnectionState(device);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return BluetoothProfile.STATE_DISCONNECTED;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return BluetoothProfile.STATE_DISCONNECTED;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Set priority of the profile
+     *
+     * <p> The device should already be paired.
+     * Priority can be one of {@link #PRIORITY_ON} orgetBluetoothManager
+     * {@link #PRIORITY_OFF},
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+     * permission.
+     *
+     * @param device Paired bluetooth device
+     * @param priority
+     * @return true if priority is set, false on error
+     * @hide
+     */
+    public boolean setPriority(BluetoothDevice device, int priority) {
+        if (DBG) log("setPriority(" + device + ", " + priority + ")");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && isValidDevice(device)) {
+                if (priority != BluetoothProfile.PRIORITY_OFF
+                        && priority != BluetoothProfile.PRIORITY_ON) {
+                    return false;
+                }
+                return mService.setPriority(device, priority);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return false;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return false;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Get the priority of the profile.
+     *
+     * <p> The priority can be any of:
+     * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
+     * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
+     *
+     * @param device Bluetooth device
+     * @return priority of the device
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public int getPriority(BluetoothDevice device) {
+        if (VDBG) log("getPriority(" + device + ")");
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && isValidDevice(device)) {
+                return mService.getPriority(device);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return BluetoothProfile.PRIORITY_OFF;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return BluetoothProfile.PRIORITY_OFF;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Helper for converting a state to a string.
+     *
+     * For debug use only - strings are not internationalized.
+     *
+     * @hide
+     */
+    public static String stateToString(int state) {
+        switch (state) {
+            case STATE_DISCONNECTED:
+                return "disconnected";
+            case STATE_CONNECTING:
+                return "connecting";
+            case STATE_CONNECTED:
+                return "connected";
+            case STATE_DISCONNECTING:
+                return "disconnecting";
+            case STATE_PLAYING:
+                return "playing";
+            case STATE_NOT_PLAYING:
+                return "not playing";
+            default:
+                return "<unknown state " + state + ">";
+        }
+    }
+
+    /**
+     * Get the volume of the device.
+     *
+     * <p> The volume is between -128 dB (mute) to 0 dB.
+     *
+     * @return volume of the hearing aid device.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public int getVolume() {
+        if (VDBG) {
+            log("getVolume()");
+        }
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()) {
+                return mService.getVolume();
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return 0;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return 0;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Tells remote device to adjust volume. Uses the following values:
+     * <ul>
+     * <li>{@link AudioManager#ADJUST_LOWER}</li>
+     * <li>{@link AudioManager#ADJUST_RAISE}</li>
+     * <li>{@link AudioManager#ADJUST_MUTE}</li>
+     * <li>{@link AudioManager#ADJUST_UNMUTE}</li>
+     * </ul>
+     *
+     * @param direction One of the supported adjust values.
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public void adjustVolume(int direction) {
+        if (DBG) log("adjustVolume(" + direction + ")");
+
+        try {
+            mServiceLock.readLock().lock();
+
+            if (mService == null) {
+                Log.w(TAG, "Proxy not attached to service");
+                return;
+            }
+
+            if (!isEnabled()) return;
+
+            mService.adjustVolume(direction);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Tells remote device to set an absolute volume.
+     *
+     * @param volume Absolute volume to be set on remote
+     * @hide
+     */
+    public void setVolume(int volume) {
+        if (DBG) Log.d(TAG, "setVolume(" + volume + ")");
+
+        try {
+            mServiceLock.readLock().lock();
+            if (mService == null) {
+                Log.w(TAG, "Proxy not attached to service");
+                return;
+            }
+
+            if (!isEnabled()) return;
+
+            mService.setVolume(volume);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Get the CustomerId of the device.
+     *
+     * @param device Bluetooth device
+     * @return the CustomerId of the device
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public long getHiSyncId(BluetoothDevice device) {
+        if (VDBG) {
+            log("getCustomerId(" + device + ")");
+        }
+        try {
+            mServiceLock.readLock().lock();
+            if (mService == null) {
+                Log.w(TAG, "Proxy not attached to service");
+                return HI_SYNC_ID_INVALID;
+            }
+
+            if (!isEnabled() || !isValidDevice(device)) return HI_SYNC_ID_INVALID;
+
+            return mService.getHiSyncId(device);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return HI_SYNC_ID_INVALID;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Get the side of the device.
+     *
+     * @param device Bluetooth device.
+     * @return SIDE_LEFT or SIDE_RIGHT
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public int getDeviceSide(BluetoothDevice device) {
+        if (VDBG) {
+            log("getDeviceSide(" + device + ")");
+        }
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && isValidDevice(device)) {
+                return mService.getDeviceSide(device);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return SIDE_LEFT;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return SIDE_LEFT;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Get the mode of the device.
+     *
+     * @param device Bluetooth device
+     * @return MODE_MONAURAL or MODE_BINAURAL
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public int getDeviceMode(BluetoothDevice device) {
+        if (VDBG) {
+            log("getDeviceMode(" + device + ")");
+        }
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null && isEnabled()
+                    && isValidDevice(device)) {
+                return mService.getDeviceMode(device);
+            }
+            if (mService == null) Log.w(TAG, "Proxy not attached to service");
+            return MODE_MONAURAL;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
+            return MODE_MONAURAL;
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+    }
+
+    private final ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            if (DBG) Log.d(TAG, "Proxy object connected");
+            try {
+                mServiceLock.writeLock().lock();
+                mService = IBluetoothHearingAid.Stub.asInterface(Binder.allowBlocking(service));
+            } finally {
+                mServiceLock.writeLock().unlock();
+            }
+
+            if (mServiceListener != null) {
+                mServiceListener.onServiceConnected(BluetoothProfile.HEARING_AID,
+                                                    BluetoothHearingAid.this);
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            if (DBG) Log.d(TAG, "Proxy object disconnected");
+            try {
+                mServiceLock.writeLock().lock();
+                mService = null;
+            } finally {
+                mServiceLock.writeLock().unlock();
+            }
+            if (mServiceListener != null) {
+                mServiceListener.onServiceDisconnected(BluetoothProfile.HEARING_AID);
+            }
+        }
+    };
+
+    private boolean isEnabled() {
+        if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
+        return false;
+    }
+
+    private boolean isValidDevice(BluetoothDevice device) {
+        if (device == null) return false;
+
+        if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+        return false;
+    }
+
+    private static void log(String msg) {
+        Log.d(TAG, msg);
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 0e2263f..656188f 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -165,12 +165,19 @@
     public static final int OPP = 20;
 
     /**
+     * Hearing Aid Device
+     *
+     * @hide
+     */
+    int HEARING_AID = 21;
+
+    /**
      * Max profile ID. This value should be updated whenever a new profile is added to match
      * the largest value assigned to a profile.
      *
      * @hide
      */
-    public static final int MAX_PROFILE_ID = 20;
+    int MAX_PROFILE_ID = 21;
 
     /**
      * Default priority for devices that we try to auto-connect to and
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 76cb3f5..0a0d214 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -79,6 +79,9 @@
             ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
     public static final ParcelUuid SAP =
             ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB");
+    /* TODO: b/69623109 update this value. It will change to 16bit UUID!! */
+    public static final ParcelUuid HearingAid =
+            ParcelUuid.fromString("7312C48F-22CC-497F-85FD-A0616A3B9E05");
 
     public static final ParcelUuid BASE_UUID =
             ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS
new file mode 100644
index 0000000..b8fea55
--- /dev/null
+++ b/core/java/android/hardware/OWNERS
@@ -0,0 +1,7 @@
+# Camera
+per-file *Camera* = cychen@google.com
+per-file *Camera* = epeev@google.com
+per-file *Camera* = etalvala@google.com
+per-file *Camera* = shuzhenwang@google.com
+per-file *Camera* = yinchiayeh@google.com
+per-file *Camera* = zhijunhe@google.com
diff --git a/core/java/android/hardware/camera2/OWNERS b/core/java/android/hardware/camera2/OWNERS
new file mode 100644
index 0000000..18acfee
--- /dev/null
+++ b/core/java/android/hardware/camera2/OWNERS
@@ -0,0 +1,6 @@
+cychen@google.com
+epeev@google.com
+etalvala@google.com
+shuzhenwang@google.com
+yinchiayeh@google.com
+zhijunhe@google.com
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 166342d..7dde2ed 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -112,8 +112,14 @@
      * <p/>
      * For a disconnect event, the boolean extra EXTRA_NO_CONNECTIVITY
      * is set to {@code true} if there are no connected networks at all.
+     *
+     * @deprecated apps should use the more versatile {@link #requestNetwork},
+     *             {@link #registerNetworkCallback} or {@link #registerDefaultNetworkCallback}
+     *             functions instead for faster and more detailed updates about the network
+     *             changes they care about.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
     public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
 
     /**
@@ -2685,6 +2691,32 @@
          * satisfying the request changes.
          *
          * @param network The {@link Network} of the satisfying network.
+         * @param networkCapabilities The {@link NetworkCapabilities} of the satisfying network.
+         * @param linkProperties The {@link LinkProperties} of the satisfying network.
+         * @hide
+         */
+        public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
+                LinkProperties linkProperties) {
+            // Internally only this method is called when a new network is available, and
+            // it calls the callback in the same way and order that older versions used
+            // to call so as not to change the behavior.
+            onAvailable(network);
+            if (!networkCapabilities.hasCapability(
+                    NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)) {
+                onNetworkSuspended(network);
+            }
+            onCapabilitiesChanged(network, networkCapabilities);
+            onLinkPropertiesChanged(network, linkProperties);
+        }
+
+        /**
+         * Called when the framework connects and has declared a new network ready for use.
+         * This callback may be called more than once if the {@link Network} that is
+         * satisfying the request changes. This will always immediately be followed by a
+         * call to {@link #onCapabilitiesChanged(Network, NetworkCapabilities)} then by a
+         * call to {@link #onLinkPropertiesChanged(Network, LinkProperties)}.
+         *
+         * @param network The {@link Network} of the satisfying network.
          */
         public void onAvailable(Network network) {}
 
@@ -2727,7 +2759,8 @@
          * changes capabilities but still satisfies the stated need.
          *
          * @param network The {@link Network} whose capabilities have changed.
-         * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this network.
+         * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this
+         *                            network.
          */
         public void onCapabilitiesChanged(Network network,
                 NetworkCapabilities networkCapabilities) {}
@@ -2743,7 +2776,7 @@
 
         /**
          * Called when the network the framework connected to for this request
-         * goes into {@link NetworkInfo.DetailedState.SUSPENDED}.
+         * goes into {@link NetworkInfo.State#SUSPENDED}.
          * This generally means that while the TCP connections are still live,
          * temporarily network data fails to transfer.  Specifically this is used
          * on cellular networks to mask temporary outages when driving through
@@ -2754,9 +2787,8 @@
 
         /**
          * Called when the network the framework connected to for this request
-         * returns from a {@link NetworkInfo.DetailedState.SUSPENDED} state.
-         * This should always be preceeded by a matching {@code onNetworkSuspended}
-         * call.
+         * returns from a {@link NetworkInfo.State#SUSPENDED} state. This should always be
+         * preceded by a matching {@link NetworkCallback#onNetworkSuspended} call.
          * @hide
          */
         public void onNetworkResumed(Network network) {}
@@ -2865,7 +2897,9 @@
                     break;
                 }
                 case CALLBACK_AVAILABLE: {
-                    callback.onAvailable(network);
+                    NetworkCapabilities cap = getObject(message, NetworkCapabilities.class);
+                    LinkProperties lp = getObject(message, LinkProperties.class);
+                    callback.onAvailable(network, cap, lp);
                     break;
                 }
                 case CALLBACK_LOSING: {
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
index 6a262e2..8599f47 100644
--- a/core/java/android/net/IpSecConfig.java
+++ b/core/java/android/net/IpSecConfig.java
@@ -218,6 +218,25 @@
     @VisibleForTesting
     public IpSecConfig() {}
 
+    /** Copy constructor */
+    @VisibleForTesting
+    public IpSecConfig(IpSecConfig c) {
+        mMode = c.mMode;
+        mSourceAddress = c.mSourceAddress;
+        mDestinationAddress = c.mDestinationAddress;
+        mNetwork = c.mNetwork;
+        mSpiResourceId = c.mSpiResourceId;
+        mEncryption = c.mEncryption;
+        mAuthentication = c.mAuthentication;
+        mAuthenticatedEncryption = c.mAuthenticatedEncryption;
+        mEncapType = c.mEncapType;
+        mEncapSocketResourceId = c.mEncapSocketResourceId;
+        mEncapRemotePort = c.mEncapRemotePort;
+        mNattKeepaliveInterval = c.mNattKeepaliveInterval;
+        mMarkValue = c.mMarkValue;
+        mMarkMask = c.mMarkMask;
+    }
+
     private IpSecConfig(Parcel in) {
         mMode = in.readInt();
         mSourceAddress = in.readString();
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 38759a9..60e96f9 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -84,9 +84,11 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface EncapType {}
 
-    private IpSecTransform(Context context, IpSecConfig config) {
+    /** @hide */
+    @VisibleForTesting
+    public IpSecTransform(Context context, IpSecConfig config) {
         mContext = context;
-        mConfig = config;
+        mConfig = new IpSecConfig(config);
         mResourceId = INVALID_RESOURCE_ID;
     }
 
@@ -143,6 +145,18 @@
     }
 
     /**
+     * Equals method used for testing
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public static boolean equals(IpSecTransform lhs, IpSecTransform rhs) {
+        if (lhs == null || rhs == null) return (lhs == rhs);
+        return IpSecConfig.equals(lhs.getConfig(), rhs.getConfig())
+                && lhs.mResourceId == rhs.mResourceId;
+    }
+
+    /**
      * Deactivate this {@code IpSecTransform} and free allocated resources.
      *
      * <p>Deactivating a transform while it is still applied to a socket will result in errors on
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 8e05cfa..bae373d 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -116,6 +116,7 @@
             NET_CAPABILITY_NOT_ROAMING,
             NET_CAPABILITY_FOREGROUND,
             NET_CAPABILITY_NOT_CONGESTED,
+            NET_CAPABILITY_NOT_SUSPENDED,
     })
     public @interface NetCapability { }
 
@@ -239,7 +240,6 @@
     /**
      * Indicates that this network is available for use by apps, and not a network that is being
      * kept up in the background to facilitate fast network switching.
-     * @hide
      */
     public static final int NET_CAPABILITY_FOREGROUND = 19;
 
@@ -252,8 +252,20 @@
      */
     public static final int NET_CAPABILITY_NOT_CONGESTED = 20;
 
+    /**
+     * Indicates that this network is not currently suspended.
+     * <p>
+     * When a network is suspended, the network's IP addresses and any connections
+     * established on the network remain valid, but the network is temporarily unable
+     * to transfer data. This can happen, for example, if a cellular network experiences
+     * a temporary loss of signal, such as when driving through a tunnel, etc.
+     * A network with this capability is not suspended, so is expected to be able to
+     * transfer data.
+     */
+    public static final int NET_CAPABILITY_NOT_SUSPENDED = 21;
+
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
-    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_CONGESTED;
+    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_SUSPENDED;
 
     /**
      * Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -262,12 +274,13 @@
     private static final long MUTABLE_CAPABILITIES =
             // TRUSTED can change when user explicitly connects to an untrusted network in Settings.
             // http://b/18206275
-            (1 << NET_CAPABILITY_TRUSTED) |
-            (1 << NET_CAPABILITY_VALIDATED) |
-            (1 << NET_CAPABILITY_CAPTIVE_PORTAL) |
-            (1 << NET_CAPABILITY_NOT_ROAMING) |
-            (1 << NET_CAPABILITY_FOREGROUND) |
-            (1 << NET_CAPABILITY_NOT_CONGESTED);
+            (1 << NET_CAPABILITY_TRUSTED)
+            | (1 << NET_CAPABILITY_VALIDATED)
+            | (1 << NET_CAPABILITY_CAPTIVE_PORTAL)
+            | (1 << NET_CAPABILITY_NOT_ROAMING)
+            | (1 << NET_CAPABILITY_FOREGROUND)
+            | (1 << NET_CAPABILITY_NOT_CONGESTED)
+            | (1 << NET_CAPABILITY_NOT_SUSPENDED);
 
     /**
      * Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -1299,6 +1312,7 @@
             case NET_CAPABILITY_NOT_ROAMING:    return "NOT_ROAMING";
             case NET_CAPABILITY_FOREGROUND:     return "FOREGROUND";
             case NET_CAPABILITY_NOT_CONGESTED:  return "NOT_CONGESTED";
+            case NET_CAPABILITY_NOT_SUSPENDED:  return "NOT_SUSPENDED";
             default:                            return Integer.toString(capability);
         }
     }
diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS
index 3a40cf4..3cd37bf 100644
--- a/core/java/android/net/OWNERS
+++ b/core/java/android/net/OWNERS
@@ -1,3 +1,5 @@
+set noparent
+
 ek@google.com
 jsharkey@android.com
 jchalard@google.com
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 5d7cf1e..7cd58e8 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2240,12 +2240,16 @@
     public static final int DATA_CONNECTION_LTE = 13;
     public static final int DATA_CONNECTION_EHRPD = 14;
     public static final int DATA_CONNECTION_HSPAP = 15;
-    public static final int DATA_CONNECTION_OTHER = 16;
+    public static final int DATA_CONNECTION_GSM = 16;
+    public static final int DATA_CONNECTION_TD_SCDMA = 17;
+    public static final int DATA_CONNECTION_IWLAN = 18;
+    public static final int DATA_CONNECTION_LTE_CA = 19;
+    public static final int DATA_CONNECTION_OTHER = 20;
 
     static final String[] DATA_CONNECTION_NAMES = {
         "none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
         "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte",
-        "ehrpd", "hspap", "other"
+        "ehrpd", "hspap", "gsm", "td_scdma", "iwlan", "lte_ca", "other"
     };
 
     public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1;
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index cdee110..228fe7a 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -29,7 +29,13 @@
 
     private static final NativeAllocationRegistry sNativeRegistry;
 
-    /** @hide */
+    /**
+     * Create and initialize a HwBinder object and the native objects
+     * used to allow this to participate in hwbinder transactions.
+     *
+     * @hide
+     */
+    @SystemApi
     public HwBinder() {
         native_setup();
 
@@ -44,12 +50,28 @@
             int code, HwParcel request, HwParcel reply, int flags)
         throws RemoteException;
 
-    /** @hide */
+    /**
+     * Process a hwbinder transaction.
+     *
+     * @param code interface specific code for interface.
+     * @param request parceled transaction
+     * @param reply object to parcel reply into
+     * @param flags transaction flags to be chosen by wire protocol
+     *
+     * @hide
+     */
+    @SystemApi
     public abstract void onTransact(
             int code, HwParcel request, HwParcel reply, int flags)
         throws RemoteException;
 
-    /** @hide */
+    /**
+     * Registers this service with the hwservicemanager.
+     *
+     * @param serviceName instance name of the service
+     * @hide
+     */
+    @SystemApi
     public native final void registerService(String serviceName)
         throws RemoteException;
 
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 65e9473..5e23932 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -71,6 +71,7 @@
     Bundle getUserRestrictions(int userHandle);
     boolean hasBaseUserRestriction(String restrictionKey, int userHandle);
     boolean hasUserRestriction(in String restrictionKey, int userHandle);
+    boolean hasUserRestrictionOnAnyUser(in String restrictionKey);
     void setUserRestriction(String key, boolean value, int userHandle);
     void setApplicationRestrictions(in String packageName, in Bundle restrictions,
             int userHandle);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 7e7af1a..1856200 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1678,6 +1678,18 @@
     }
 
     /**
+     * @hide
+     * Returns whether any user on the device has the given user restriction set.
+     */
+    public boolean hasUserRestrictionOnAnyUser(String restrictionKey) {
+        try {
+            return mService.hasUserRestrictionOnAnyUser(restrictionKey);
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Return the serial number for a user.  This is a device-unique
      * number assigned to that user; if the user is deleted and then a new
      * user created, the new users will not be given the same serial number.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1223271..05ab486 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5385,6 +5385,17 @@
                 "autofill_user_data_max_field_classification_size";
 
         /**
+         * Defines value returned by
+         * {@link android.service.autofill.UserData#getMaxCategoryCount()}.
+         *
+         * @hide
+         */
+        @SystemApi
+        @TestApi
+        public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT =
+                "autofill_user_data_max_category_count";
+
+        /**
          * Defines value returned by {@link android.service.autofill.UserData#getMaxValueLength()}.
          *
          * @hide
@@ -5443,32 +5454,6 @@
          */
         public static final String ENABLED_INPUT_METHODS = "enabled_input_methods";
 
-        private static final Validator ENABLED_INPUT_METHODS_VALIDATOR = new Validator() {
-            @Override
-            public boolean validate(String value) {
-                if (value == null) {
-                    return false;
-                }
-                String[] inputMethods = value.split(":");
-                boolean valid = true;
-                for (String inputMethod : inputMethods) {
-                    if (inputMethod.length() == 0) {
-                        return false;
-                    }
-                    String[] subparts = inputMethod.split(";");
-                    for (String subpart : subparts) {
-                        // allow either a non negative integer or a ComponentName
-                        valid |= (NON_NEGATIVE_INTEGER_VALIDATOR.validate(subpart)
-                                || COMPONENT_NAME_VALIDATOR.validate(subpart));
-                    }
-                    if (!valid) {
-                        return false;
-                    }
-                }
-                return valid;
-            }
-        };
-
         /**
          * List of system input methods that are currently disabled.  This is a string
          * containing the IDs of all disabled input methods, each ID separated
@@ -7709,7 +7694,6 @@
             ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
             ENABLED_ACCESSIBILITY_SERVICES,
             ENABLED_VR_LISTENERS,
-            ENABLED_INPUT_METHODS,
             TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
             TOUCH_EXPLORATION_ENABLED,
             ACCESSIBILITY_ENABLED,
@@ -7815,7 +7799,6 @@
             VALIDATORS.put(ENABLED_ACCESSIBILITY_SERVICES,
                     ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR);
             VALIDATORS.put(ENABLED_VR_LISTENERS, ENABLED_VR_LISTENERS_VALIDATOR);
-            VALIDATORS.put(ENABLED_INPUT_METHODS, ENABLED_INPUT_METHODS_VALIDATOR);
             VALIDATORS.put(TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
                     TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR);
             VALIDATORS.put(TOUCH_EXPLORATION_ENABLED, TOUCH_EXPLORATION_ENABLED_VALIDATOR);
@@ -11382,7 +11365,8 @@
                 "chained_battery_attribution_enabled";
 
         /**
-         * The packages whitelisted to be run in autofill compatibility mode.
+         * The packages whitelisted to be run in autofill compatibility mode. The list
+         * of packages is ":" colon delimited.
          *
          * @hide
          */
@@ -12097,6 +12081,28 @@
                 "enable_gnss_raw_meas_full_tracking";
 
         /**
+         * Whether the notification should be ongoing (persistent) when a carrier app install is
+         * required.
+         *
+         * The value is a boolean (1 or 0).
+         * @hide
+         */
+        @SystemApi
+        public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT =
+                "install_carrier_app_notification_persistent";
+
+        /**
+         * The amount of time (ms) to hide the install carrier app notification after the user has
+         * ignored it. After this time passes, the notification will be shown again
+         *
+         * The value is a long
+         * @hide
+         */
+        @SystemApi
+        public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS =
+                "install_carrier_app_notification_sleep_millis";
+
+        /**
          * Whether we've enabled zram on this device. Takes effect on
          * reboot. The value "1" enables zram; "0" disables it, and
          * everything else is unspecified.
diff --git a/core/java/android/security/keystore/OWNERS b/core/java/android/security/keystore/OWNERS
new file mode 100644
index 0000000..bb487fb
--- /dev/null
+++ b/core/java/android/security/keystore/OWNERS
@@ -0,0 +1,4 @@
+aseemk@google.com
+bozhu@google.com
+dementyev@google.com
+robertberry@google.com
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index f644887..70c4ec0 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -45,7 +45,6 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.util.Map;
 
 /**
  * {@link ServiceInfo} and meta-data about an {@link AutofillService}.
@@ -80,7 +79,7 @@
     private final String mSettingsActivity;
 
     @Nullable
-    private final Map<String, Pair<Long, String>> mCompatibilityPackages;
+    private final ArrayMap<String, Pair<Long, String>> mCompatibilityPackages;
 
     public AutofillServiceInfo(Context context, ComponentName comp, int userHandle)
             throws PackageManager.NameNotFoundException {
@@ -118,7 +117,7 @@
         }
 
         String settingsActivity = null;
-        Map<String, Pair<Long, String>> compatibilityPackages = null;
+        ArrayMap<String, Pair<Long, String>> compatibilityPackages = null;
 
         try {
             final Resources resources = context.getPackageManager().getResourcesForApplication(
@@ -154,10 +153,10 @@
         mCompatibilityPackages = compatibilityPackages;
     }
 
-    private Map<String, Pair<Long, String>> parseCompatibilityPackages(XmlPullParser parser,
+    private ArrayMap<String, Pair<Long, String>> parseCompatibilityPackages(XmlPullParser parser,
             Resources resources)
             throws IOException, XmlPullParserException {
-        Map<String, Pair<Long, String>> compatibilityPackages = null;
+        ArrayMap<String, Pair<Long, String>> compatibilityPackages = null;
 
         final int outerDepth = parser.getDepth();
         int type;
@@ -229,15 +228,8 @@
         return mSettingsActivity;
     }
 
-    public boolean isCompatibilityModeRequested(String packageName, long versionCode) {
-        if (mCompatibilityPackages == null) {
-            return false;
-        }
-        final Pair<Long, String> pair = mCompatibilityPackages.get(packageName);
-        if (pair == null) {
-            return false;
-        }
-        return versionCode <= pair.first;
+    public ArrayMap<String, Pair<Long, String>> getCompatibilityPackages() {
+        return mCompatibilityPackages;
     }
 
     /**
diff --git a/core/java/android/service/autofill/DateTransformation.java b/core/java/android/service/autofill/DateTransformation.java
index 4e1425d..ec24a09 100644
--- a/core/java/android/service/autofill/DateTransformation.java
+++ b/core/java/android/service/autofill/DateTransformation.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.TestApi;
+import android.icu.text.DateFormat;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -30,7 +31,6 @@
 
 import com.android.internal.util.Preconditions;
 
-import java.text.DateFormat;
 import java.util.Date;
 
 /**
diff --git a/core/java/android/service/autofill/DateValueSanitizer.java b/core/java/android/service/autofill/DateValueSanitizer.java
index 0f7b540..4f797f4 100644
--- a/core/java/android/service/autofill/DateValueSanitizer.java
+++ b/core/java/android/service/autofill/DateValueSanitizer.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.icu.text.DateFormat;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -28,7 +29,6 @@
 
 import com.android.internal.util.Preconditions;
 
-import java.text.DateFormat;
 import java.util.Date;
 
 /**
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
index cd1efd6..5bf56cb9 100644
--- a/core/java/android/service/autofill/FieldClassification.java
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -108,21 +108,21 @@
      */
     public static final class Match {
 
-        private final String mRemoteId;
+        private final String mCategoryId;
         private final float mScore;
 
         /** @hide */
-        public Match(String remoteId, float score) {
-            mRemoteId = Preconditions.checkNotNull(remoteId);
+        public Match(String categoryId, float score) {
+            mCategoryId = Preconditions.checkNotNull(categoryId);
             mScore = score;
         }
 
         /**
-         * Gets the remote id of the {@link UserData} entry.
+         * Gets the category id of the {@link UserData} entry.
          */
         @NonNull
-        public String getRemoteId() {
-            return mRemoteId;
+        public String getCategoryId() {
+            return mCategoryId;
         }
 
         /**
@@ -149,13 +149,13 @@
         public String toString() {
             if (!sDebug) return super.toString();
 
-            final StringBuilder string = new StringBuilder("Match: remoteId=");
-            Helper.appendRedacted(string, mRemoteId);
+            final StringBuilder string = new StringBuilder("Match: categoryId=");
+            Helper.appendRedacted(string, mCategoryId);
             return string.append(", score=").append(mScore).toString();
         }
 
         private void writeToParcel(@NonNull Parcel parcel) {
-            parcel.writeString(mRemoteId);
+            parcel.writeString(mCategoryId);
             parcel.writeFloat(mScore);
         }
 
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 6bab6aa..a1dd1f8 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -15,6 +15,7 @@
  */
 package android.service.autofill;
 
+import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT;
 import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE;
 import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE;
 import static android.provider.Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH;
@@ -31,6 +32,7 @@
 import android.provider.Settings;
 import android.service.autofill.FieldClassification.Match;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.Log;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.Helper;
@@ -48,23 +50,24 @@
 
     private static final String TAG = "UserData";
 
-    private static final int DEFAULT_MAX_USER_DATA_SIZE = 10;
+    private static final int DEFAULT_MAX_USER_DATA_SIZE = 50;
+    private static final int DEFAULT_MAX_CATEGORY_COUNT = 10;
     private static final int DEFAULT_MAX_FIELD_CLASSIFICATION_IDS_SIZE = 10;
-    private static final int DEFAULT_MIN_VALUE_LENGTH = 5;
+    private static final int DEFAULT_MIN_VALUE_LENGTH = 3;
     private static final int DEFAULT_MAX_VALUE_LENGTH = 100;
 
     private final String mId;
     private final String mAlgorithm;
     private final Bundle mAlgorithmArgs;
-    private final String[] mRemoteIds;
+    private final String[] mCategoryIds;
     private final String[] mValues;
 
     private UserData(Builder builder) {
         mId = builder.mId;
         mAlgorithm = builder.mAlgorithm;
         mAlgorithmArgs = builder.mAlgorithmArgs;
-        mRemoteIds = new String[builder.mRemoteIds.size()];
-        builder.mRemoteIds.toArray(mRemoteIds);
+        mCategoryIds = new String[builder.mCategoryIds.size()];
+        builder.mCategoryIds.toArray(mCategoryIds);
         mValues = new String[builder.mValues.size()];
         builder.mValues.toArray(mValues);
     }
@@ -91,8 +94,8 @@
     }
 
     /** @hide */
-    public String[] getRemoteIds() {
-        return mRemoteIds;
+    public String[] getCategoryIds() {
+        return mCategoryIds;
     }
 
     /** @hide */
@@ -106,11 +109,11 @@
         pw.print(prefix); pw.print("Algorithm: "); pw.print(mAlgorithm);
         pw.print(" Args: "); pw.println(mAlgorithmArgs);
 
-        // Cannot disclose remote ids or values because they could contain PII
-        pw.print(prefix); pw.print("Remote ids size: "); pw.println(mRemoteIds.length);
-        for (int i = 0; i < mRemoteIds.length; i++) {
+        // Cannot disclose field ids or values because they could contain PII
+        pw.print(prefix); pw.print("Field ids size: "); pw.println(mCategoryIds.length);
+        for (int i = 0; i < mCategoryIds.length; i++) {
             pw.print(prefix); pw.print(prefix); pw.print(i); pw.print(": ");
-            pw.println(Helper.getRedacted(mRemoteIds[i]));
+            pw.println(Helper.getRedacted(mCategoryIds[i]));
         }
         pw.print(prefix); pw.print("Values size: "); pw.println(mValues.length);
         for (int i = 0; i < mValues.length; i++) {
@@ -124,6 +127,7 @@
         pw.print(prefix); pw.print("maxUserDataSize: "); pw.println(getMaxUserDataSize());
         pw.print(prefix); pw.print("maxFieldClassificationIdsSize: ");
         pw.println(getMaxFieldClassificationIdsSize());
+        pw.print(prefix); pw.print("maxCategoryCount: "); pw.println(getMaxCategoryCount());
         pw.print(prefix); pw.print("minValueLength: "); pw.println(getMinValueLength());
         pw.print(prefix); pw.print("maxValueLength: "); pw.println(getMaxValueLength());
     }
@@ -133,44 +137,59 @@
      */
     public static final class Builder {
         private final String mId;
-        private final ArrayList<String> mRemoteIds;
+        private final ArrayList<String> mCategoryIds;
         private final ArrayList<String> mValues;
         private String mAlgorithm;
         private Bundle mAlgorithmArgs;
         private boolean mDestroyed;
 
+        // Non-persistent array used to limit the number of unique ids.
+        private final ArraySet<String> mUniqueCategoryIds;
+
         /**
          * Creates a new builder for the user data used for <a href="#FieldClassification">field
          * classification</a>.
          *
-         * <p>The user data must contain at least one pair of {@code remoteId} -> {@code value}, and
-         * more pairs can be added through the {@link #add(String, String)} method.
+         * <p>The user data must contain at least one pair of {@code value} -> {@code categoryId},
+         * and more pairs can be added through the {@link #add(String, String)} method. For example:
+         *
+         * <pre class="prettyprint">
+         * new UserData.Builder("v1", "Bart Simpson", "name")
+         *   .add("bart.simpson@example.com", "email")
+         *   .add("el_barto@example.com", "email")
+         *   .build();
+         * </pre>
          *
          * @param id id used to identify the whole {@link UserData} object. This id is also returned
          * by {@link AutofillManager#getUserDataId()}, which can be used to check if the
          * {@link UserData} is up-to-date without fetching the whole object (through
          * {@link AutofillManager#getUserData()}).
-         * @param remoteId unique string used to identify a user data value.
+         *
          * @param value value of the user data.
+         * @param categoryId string used to identify the category the value is associated with.
          *
          * @throws IllegalArgumentException if any of the following occurs:
          * <ol>
-         *   <li>{@code id} is empty
-         *   <li>{@code remoteId} is empty
-         *   <li>{@code value} is empty
-         *   <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}
-         *   <li>the length of {@code value} is higher than {@link UserData#getMaxValueLength()}
+         *   <li>{@code id} is empty</li>
+         *   <li>{@code categoryId} is empty</li>
+         *   <li>{@code value} is empty</li>
+         *   <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}</li>
+         *   <li>the length of {@code value} is higher than
+         *       {@link UserData#getMaxValueLength()}</li>
          * </ol>
+         *
          */
-        public Builder(@NonNull String id, @NonNull String remoteId, @NonNull String value) {
+        // TODO(b/70407264): ignore entry instead of throwing exception when settings changed
+        public Builder(@NonNull String id, @NonNull String value, @NonNull String categoryId) {
             mId = checkNotEmpty("id", id);
-            checkNotEmpty("remoteId", remoteId);
+            checkNotEmpty("categoryId", categoryId);
             checkValidValue(value);
-            final int capacity = getMaxUserDataSize();
-            mRemoteIds = new ArrayList<>(capacity);
-            mValues = new ArrayList<>(capacity);
-            mRemoteIds.add(remoteId);
-            mValues.add(value);
+            final int maxUserDataSize = getMaxUserDataSize();
+            mCategoryIds = new ArrayList<>(maxUserDataSize);
+            mValues = new ArrayList<>(maxUserDataSize);
+            mUniqueCategoryIds = new ArraySet<>(getMaxCategoryCount());
+
+            addMapping(value, categoryId);
         }
 
         /**
@@ -190,6 +209,7 @@
          */
         public Builder setFieldClassificationAlgorithm(@Nullable String name,
                 @Nullable Bundle args) {
+            throwIfDestroyed();
             mAlgorithm = name;
             mAlgorithmArgs = args;
             return this;
@@ -198,37 +218,58 @@
         /**
          * Adds a new value for user data.
          *
-         * @param remoteId unique string used to identify the user data.
          * @param value value of the user data.
+         * @param categoryId string used to identify the category the value is associated with.
          *
-         * @throws IllegalStateException if {@link #build()} or
-         * {@link #add(String, String)} with the same {@code remoteId} has already
-         * been called, or if the number of values add (i.e., calls made to this method plus
-         * constructor) is more than {@link UserData#getMaxUserDataSize()}.
+         * @throws IllegalStateException if:
+         * <ol>
+         *   <li>{@link #build()} already called</li>
+         *   <li>the {@code value} has already been added</li>
+         *   <li>the number of unique {@code categoryId} values added so far is more than
+         *       {@link UserData#getMaxCategoryCount()}</li>
+         *   <li>the number of {@code values} added so far is is more than
+         *       {@link UserData#getMaxUserDataSize()}</li>
+         * </ol>
          *
-         * @throws IllegalArgumentException if {@code remoteId} or {@code value} are empty or if the
-         * length of {@code value} is lower than {@link UserData#getMinValueLength()}
-         * or higher than {@link UserData#getMaxValueLength()}.
+         * @throws IllegalArgumentException if any of the following occurs:
+         * <ol>
+         *   <li>{@code id} is empty</li>
+         *   <li>{@code categoryId} is empty</li>
+         *   <li>{@code value} is empty</li>
+         *   <li>the length of {@code value} is lower than {@link UserData#getMinValueLength()}</li>
+         *   <li>the length of {@code value} is higher than
+         *       {@link UserData#getMaxValueLength()}</li>
+         * </ol>
          */
-        public Builder add(@NonNull String remoteId, @NonNull String value) {
+        // TODO(b/70407264): ignore entry instead of throwing exception when settings changed
+        public Builder add(@NonNull String value, @NonNull String categoryId) {
             throwIfDestroyed();
-            checkNotEmpty("remoteId", remoteId);
+            checkNotEmpty("categoryId", categoryId);
             checkValidValue(value);
 
-            Preconditions.checkState(!mRemoteIds.contains(remoteId),
-                    // Don't include remoteId on message because it could contain PII
-                    "already has entry with same remoteId");
+            if (!mUniqueCategoryIds.contains(categoryId)) {
+                // New category - check size
+                Preconditions.checkState(mUniqueCategoryIds.size() < getMaxCategoryCount(),
+                        "already added " + mUniqueCategoryIds.size() + " unique category ids");
+
+            }
+
             Preconditions.checkState(!mValues.contains(value),
-                    // Don't include remoteId on message because it could contain PII
+                    // Don't include value on message because it could contain PII
                     "already has entry with same value");
-            Preconditions.checkState(mRemoteIds.size() < getMaxUserDataSize(),
-                    "already added " + mRemoteIds.size() + " elements");
-            mRemoteIds.add(remoteId);
-            mValues.add(value);
+            Preconditions.checkState(mValues.size() < getMaxUserDataSize(),
+                    "already added " + mValues.size() + " elements");
+            addMapping(value, categoryId);
 
             return this;
         }
 
+        private void addMapping(@NonNull String value, @NonNull String categoryId) {
+            mCategoryIds.add(categoryId);
+            mValues.add(value);
+            mUniqueCategoryIds.add(categoryId);
+        }
+
         private String checkNotEmpty(@NonNull String name, @Nullable String value) {
             Preconditions.checkNotNull(value);
             Preconditions.checkArgument(!TextUtils.isEmpty(value), "%s cannot be empty", name);
@@ -273,9 +314,9 @@
 
         final StringBuilder builder = new StringBuilder("UserData: [id=").append(mId)
                 .append(", algorithm=").append(mAlgorithm);
-        // Cannot disclose remote ids or values because they could contain PII
-        builder.append(", remoteIds=");
-        Helper.appendRedacted(builder, mRemoteIds);
+        // Cannot disclose category ids or values because they could contain PII
+        builder.append(", categoryIds=");
+        Helper.appendRedacted(builder, mCategoryIds);
         builder.append(", values=");
         Helper.appendRedacted(builder, mValues);
         return builder.append("]").toString();
@@ -293,7 +334,7 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeString(mId);
-        parcel.writeStringArray(mRemoteIds);
+        parcel.writeStringArray(mCategoryIds);
         parcel.writeStringArray(mValues);
         parcel.writeString(mAlgorithm);
         parcel.writeBundle(mAlgorithmArgs);
@@ -307,12 +348,12 @@
             // the system obeys the contract of the builder to avoid attacks
             // using specially crafted parcels.
             final String id = parcel.readString();
-            final String[] remoteIds = parcel.readStringArray();
+            final String[] categoryIds = parcel.readStringArray();
             final String[] values = parcel.readStringArray();
-            final Builder builder = new Builder(id, remoteIds[0], values[0])
+            final Builder builder = new Builder(id, values[0], categoryIds[0])
                     .setFieldClassificationAlgorithm(parcel.readString(), parcel.readBundle());
-            for (int i = 1; i < remoteIds.length; i++) {
-                builder.add(remoteIds[i], values[i]);
+            for (int i = 1; i < categoryIds.length; i++) {
+                builder.add(values[i], categoryIds[i]);
             }
             return builder.build();
         }
@@ -340,6 +381,14 @@
     }
 
     /**
+     * Gets the maximum number of unique category ids that can be passed to
+     * the builder's constructor and {@link Builder#add(String, String)}.
+     */
+    public static int getMaxCategoryCount() {
+        return getInt(AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT, DEFAULT_MAX_CATEGORY_COUNT);
+    }
+
+    /**
      * Gets the minimum length of values passed to the builder's constructor or
      * or {@link Builder#add(String, String)}.
      */
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index dbe4157..6fa5312 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -322,12 +322,6 @@
      */
     public static Metrics isBoring(CharSequence text, TextPaint paint,
             TextDirectionHeuristic textDir, Metrics metrics) {
-        return isBoring(text, null /* precomputed */, paint, textDir, metrics);
-    }
-
-    /** @hide */
-    public static Metrics isBoring(CharSequence text, PrecomputedText precomputed, TextPaint paint,
-            TextDirectionHeuristic textDir, Metrics metrics) {
         final int textLength = text.length();
         if (hasAnyInterestingChars(text, textLength)) {
            return null;  // There are some interesting characters. Not boring.
@@ -350,17 +344,18 @@
             fm.reset();
         }
 
-        if (precomputed != null) {
+        TextLine line = TextLine.obtain();
+        line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
+                Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
+        if (text instanceof MeasuredText) {
+            MeasuredText mt = (MeasuredText) text;
             // Reaching here means there is only one paragraph.
-            MeasuredParagraph mp = precomputed.getMeasuredParagraph(0);
+            MeasuredParagraph mp = mt.getMeasuredParagraph(0);
             fm.width = (int) Math.ceil(mp.getWidth(0, mp.getTextLength()));
         } else {
-            TextLine line = TextLine.obtain();
-            line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
-                    Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
             fm.width = (int) Math.ceil(line.metrics(fm));
-            TextLine.recycle(line);
         }
+        TextLine.recycle(line);
 
         return fm;
     }
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 10444f0..18431ca 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -363,7 +363,7 @@
                          @JustificationMode int justificationMode,
                          @Nullable TextUtils.TruncateAt ellipsize,
                          @IntRange(from = 0) int ellipsizedWidth) {
-        super(createEllipsizer(ellipsize, display), null /* precomputed */,
+        super(createEllipsizer(ellipsize, display),
               paint, width, align, textDir, spacingmult, spacingadd);
 
         final Builder b = Builder.obtain(base, paint, width)
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index d5d3590..aa97b2a 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -245,13 +245,6 @@
     protected Layout(CharSequence text, TextPaint paint,
                      int width, Alignment align, TextDirectionHeuristic textDir,
                      float spacingMult, float spacingAdd) {
-        this(text, null /* precomputed */, paint, width, align, textDir, spacingMult, spacingAdd);
-    }
-
-    /** @hide */
-    protected Layout(CharSequence text, PrecomputedText precomputed, TextPaint paint,
-                     int width, Alignment align, TextDirectionHeuristic textDir,
-                     float spacingMult, float spacingAdd) {
 
         if (width < 0)
             throw new IllegalArgumentException("Layout: " + width + " < 0");
@@ -266,7 +259,6 @@
         }
 
         mText = text;
-        mPrecomputed = precomputed;
         mPaint = paint;
         mWidth = width;
         mAlignment = align;
@@ -570,7 +562,7 @@
                 // XXX: assumes there's nothing additional to be done
                 canvas.drawText(buf, start, end, x, lbaseline, paint);
             } else {
-                tl.set(paint, buf, mPrecomputed, start, end, dir, directions, hasTab, tabStops);
+                tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops);
                 if (justify) {
                     tl.justify(right - left - indentWidth);
                 }
@@ -2272,7 +2264,6 @@
     }
 
     private CharSequence mText;
-    private PrecomputedText mPrecomputed;
     private TextPaint mPaint;
     private TextPaint mWorkPaint = new TextPaint();
     private int mWidth;
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
new file mode 100644
index 0000000..bb7a9e0
--- /dev/null
+++ b/core/java/android/text/MeasuredText.java
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2017 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 android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.util.IntArray;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+
+/**
+ * A text which has already been measured.
+ */
+public class MeasuredText implements Spanned {
+    private static final char LINE_FEED = '\n';
+
+    // The original text.
+    private final @NonNull CharSequence mText;
+
+    // The inclusive start offset of the measuring target.
+    private final @IntRange(from = 0) int mStart;
+
+    // The exclusive end offset of the measuring target.
+    private final @IntRange(from = 0) int mEnd;
+
+    // The TextPaint used for measurement.
+    private final @NonNull TextPaint mPaint;
+
+    // The requested text direction.
+    private final @NonNull TextDirectionHeuristic mTextDir;
+
+    // The measured paragraph texts.
+    private final @NonNull MeasuredParagraph[] mMeasuredParagraphs;
+
+    // The sorted paragraph end offsets.
+    private final @NonNull int[] mParagraphBreakPoints;
+
+    // The break strategy for this measured text.
+    private final @Layout.BreakStrategy int mBreakStrategy;
+
+    // The hyphenation frequency for this measured text.
+    private final @Layout.HyphenationFrequency int mHyphenationFrequency;
+
+    /**
+     * A Builder for MeasuredText
+     */
+    public static final class Builder {
+        // Mandatory parameters.
+        private final @NonNull CharSequence mText;
+        private final @NonNull TextPaint mPaint;
+
+        // Members to be updated by setters.
+        private @IntRange(from = 0) int mStart;
+        private @IntRange(from = 0) int mEnd;
+        private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
+        private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
+        private @Layout.HyphenationFrequency int mHyphenationFrequency =
+                Layout.HYPHENATION_FREQUENCY_NORMAL;
+
+
+        /**
+         * Builder constructor
+         *
+         * @param text The text to be measured.
+         * @param paint The paint to be used for drawing.
+         */
+        public Builder(@NonNull CharSequence text, @NonNull TextPaint paint) {
+            Preconditions.checkNotNull(text);
+            Preconditions.checkNotNull(paint);
+
+            mText = text;
+            mPaint = paint;
+            mStart = 0;
+            mEnd = text.length();
+        }
+
+        /**
+         * Set the range of measuring target.
+         *
+         * @param start The measuring target start offset in the text.
+         * @param end The measuring target end offset in the text.
+         */
+        public @NonNull Builder setRange(@IntRange(from = 0) int start,
+                                         @IntRange(from = 0) int end) {
+            Preconditions.checkArgumentInRange(start, 0, mText.length(), "start");
+            Preconditions.checkArgumentInRange(end, 0, mText.length(), "end");
+            Preconditions.checkArgument(start <= end, "The range is reversed.");
+
+            mStart = start;
+            mEnd = end;
+            return this;
+        }
+
+        /**
+         * Set the text direction heuristic
+         *
+         * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
+         *
+         * @param textDir The text direction heuristic for resolving bidi behavior.
+         * @return this builder, useful for chaining.
+         */
+        public @NonNull Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) {
+            Preconditions.checkNotNull(textDir);
+            mTextDir = textDir;
+            return this;
+        }
+
+        /**
+         * Set the break strategy
+         *
+         * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}.
+         *
+         * @param breakStrategy The break strategy.
+         * @return this builder, useful for chaining.
+         */
+        public @NonNull Builder setBreakStrategy(@Layout.BreakStrategy int breakStrategy) {
+            mBreakStrategy = breakStrategy;
+            return this;
+        }
+
+        /**
+         * Set the hyphenation frequency
+         *
+         * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
+         *
+         * @param hyphenationFrequency The hyphenation frequency.
+         * @return this builder, useful for chaining.
+         */
+        public @NonNull Builder setHyphenationFrequency(
+                @Layout.HyphenationFrequency int hyphenationFrequency) {
+            mHyphenationFrequency = hyphenationFrequency;
+            return this;
+        }
+
+        /**
+         * Build the measured text
+         *
+         * @return the measured text.
+         */
+        public @NonNull MeasuredText build() {
+            return build(true /* build full layout result */);
+        }
+
+        /** @hide */
+        public @NonNull MeasuredText build(boolean computeLayout) {
+            final boolean needHyphenation = mBreakStrategy != Layout.BREAK_STRATEGY_SIMPLE
+                    && mHyphenationFrequency != Layout.HYPHENATION_FREQUENCY_NONE;
+
+            final IntArray paragraphEnds = new IntArray();
+            final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
+
+            int paraEnd = 0;
+            for (int paraStart = mStart; paraStart < mEnd; paraStart = paraEnd) {
+                paraEnd = TextUtils.indexOf(mText, LINE_FEED, paraStart, mEnd);
+                if (paraEnd < 0) {
+                    // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph
+                    // end.
+                    paraEnd = mEnd;
+                } else {
+                    paraEnd++;  // Includes LINE_FEED(U+000A) to the prev paragraph.
+                }
+
+                paragraphEnds.add(paraEnd);
+                measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
+                        mPaint, mText, paraStart, paraEnd, mTextDir, needHyphenation,
+                        computeLayout, null /* no recycle */));
+            }
+
+            return new MeasuredText(mText, mStart, mEnd, mPaint, mTextDir, mBreakStrategy,
+                                    mHyphenationFrequency, measuredTexts.toArray(
+                                            new MeasuredParagraph[measuredTexts.size()]),
+                                    paragraphEnds.toArray());
+        }
+    };
+
+    // Use MeasuredText.Builder instead.
+    private MeasuredText(@NonNull CharSequence text,
+                         @IntRange(from = 0) int start,
+                         @IntRange(from = 0) int end,
+                         @NonNull TextPaint paint,
+                         @NonNull TextDirectionHeuristic textDir,
+                         @Layout.BreakStrategy int breakStrategy,
+                         @Layout.HyphenationFrequency int frequency,
+                         @NonNull MeasuredParagraph[] measuredTexts,
+                         @NonNull int[] paragraphBreakPoints) {
+        mText = text;
+        mStart = start;
+        mEnd = end;
+        // Copy the paint so that we can keep the reference of typeface in native layout result.
+        mPaint = new TextPaint(paint);
+        mMeasuredParagraphs = measuredTexts;
+        mParagraphBreakPoints = paragraphBreakPoints;
+        mTextDir = textDir;
+        mBreakStrategy = breakStrategy;
+        mHyphenationFrequency = frequency;
+    }
+
+    /**
+     * Return the underlying text.
+     */
+    public @NonNull CharSequence getText() {
+        return mText;
+    }
+
+    /**
+     * Returns the inclusive start offset of measured region.
+     */
+    public @IntRange(from = 0) int getStart() {
+        return mStart;
+    }
+
+    /**
+     * Returns the exclusive end offset of measured region.
+     */
+    public @IntRange(from = 0) int getEnd() {
+        return mEnd;
+    }
+
+    /**
+     * Returns the text direction associated with char sequence.
+     */
+    public @NonNull TextDirectionHeuristic getTextDir() {
+        return mTextDir;
+    }
+
+    /**
+     * Returns the paint used to measure this text.
+     */
+    public @NonNull TextPaint getPaint() {
+        return mPaint;
+    }
+
+    /**
+     * Returns the length of the paragraph of this text.
+     */
+    public @IntRange(from = 0) int getParagraphCount() {
+        return mParagraphBreakPoints.length;
+    }
+
+    /**
+     * Returns the paragraph start offset of the text.
+     */
+    public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
+        Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+        return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
+    }
+
+    /**
+     * Returns the paragraph end offset of the text.
+     */
+    public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
+        Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
+        return mParagraphBreakPoints[paraIndex];
+    }
+
+    /** @hide */
+    public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
+        return mMeasuredParagraphs[paraIndex];
+    }
+
+    /**
+     * Returns the break strategy for this text.
+     */
+    public @Layout.BreakStrategy int getBreakStrategy() {
+        return mBreakStrategy;
+    }
+
+    /**
+     * Returns the hyphenation frequency for this text.
+     */
+    public @Layout.HyphenationFrequency int getHyphenationFrequency() {
+        return mHyphenationFrequency;
+    }
+
+    /**
+     * Returns true if the given TextPaint gives the same result of text layout for this text.
+     * @hide
+     */
+    public boolean canUseMeasuredResult(@NonNull TextPaint paint) {
+        return mPaint.getTextSize() == paint.getTextSize()
+            && mPaint.getTextSkewX() == paint.getTextSkewX()
+            && mPaint.getTextScaleX() == paint.getTextScaleX()
+            && mPaint.getLetterSpacing() == paint.getLetterSpacing()
+            && mPaint.getWordSpacing() == paint.getWordSpacing()
+            && mPaint.getFlags() == paint.getFlags()  // Maybe not all flag affects text layout.
+            && mPaint.getTextLocales() == paint.getTextLocales()  // need to be equals?
+            && mPaint.getFontVariationSettings() == paint.getFontVariationSettings()
+            && mPaint.getTypeface() == paint.getTypeface()
+            && TextUtils.equals(mPaint.getFontFeatureSettings(), paint.getFontFeatureSettings());
+    }
+
+    /** @hide */
+    public int findParaIndex(@IntRange(from = 0) int pos) {
+        // TODO: Maybe good to remove paragraph concept from MeasuredText and add substring layout
+        //       support to StaticLayout.
+        for (int i = 0; i < mParagraphBreakPoints.length; ++i) {
+            if (pos < mParagraphBreakPoints[i]) {
+                return i;
+            }
+        }
+        throw new IndexOutOfBoundsException(
+            "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1]
+            + ", gave " + pos);
+    }
+
+    /** @hide */
+    public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
+        final int paraIndex = findParaIndex(start);
+        final int paraStart = getParagraphStart(paraIndex);
+        final int paraEnd = getParagraphEnd(paraIndex);
+        if (start < paraStart || paraEnd < end) {
+            throw new RuntimeException("Cannot measured across the paragraph:"
+                + "para: (" + paraStart + ", " + paraEnd + "), "
+                + "request: (" + start + ", " + end + ")");
+        }
+        return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
+    }
+
+    /**
+     * Returns the size of native MeasuredText memory usage
+     *
+     * Note that this may not be aculate. Must be used only for testing purposes.
+     * @hide
+     */
+    public int getMemoryUsage() {
+        int r = 0;
+        for (int i = 0; i < getParagraphCount(); ++i) {
+            r += getMeasuredParagraph(i).getMemoryUsage();
+        }
+        return r;
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // Spanned overrides
+    //
+    // Just proxy for underlying mText if appropriate.
+
+    @Override
+    public <T> T[] getSpans(int start, int end, Class<T> type) {
+        if (mText instanceof Spanned) {
+            return ((Spanned) mText).getSpans(start, end, type);
+        } else {
+            return ArrayUtils.emptyArray(type);
+        }
+    }
+
+    @Override
+    public int getSpanStart(Object tag) {
+        if (mText instanceof Spanned) {
+            return ((Spanned) mText).getSpanStart(tag);
+        } else {
+            return -1;
+        }
+    }
+
+    @Override
+    public int getSpanEnd(Object tag) {
+        if (mText instanceof Spanned) {
+            return ((Spanned) mText).getSpanEnd(tag);
+        } else {
+            return -1;
+        }
+    }
+
+    @Override
+    public int getSpanFlags(Object tag) {
+        if (mText instanceof Spanned) {
+            return ((Spanned) mText).getSpanFlags(tag);
+        } else {
+            return 0;
+        }
+    }
+
+    @Override
+    public int nextSpanTransition(int start, int limit, Class type) {
+        if (mText instanceof Spanned) {
+            return ((Spanned) mText).nextSpanTransition(start, limit, type);
+        } else {
+            return mText.length();
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////////////////////////
+    // CharSequence overrides.
+    //
+    // Just proxy for underlying mText.
+
+    @Override
+    public int length() {
+        return mText.length();
+    }
+
+    @Override
+    public char charAt(int index) {
+        // TODO: Should this be index + mStart ?
+        return mText.charAt(index);
+    }
+
+    @Override
+    public CharSequence subSequence(int start, int end) {
+        // TODO: return MeasuredText.
+        // TODO: Should this be index + mStart, end + mStart ?
+        return mText.subSequence(start, end);
+    }
+
+    @Override
+    public String toString() {
+        return mText.toString();
+    }
+}
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
deleted file mode 100644
index 39fc2bd..0000000
--- a/core/java/android/text/PrecomputedText.java
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * Copyright (C) 2017 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 android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.util.IntArray;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.ArrayList;
-
-/**
- * A text which has the character metrics data
- *
- * This text holds a part of the text layout result. You can accelerate
- * {@link android.widget.TextView} or {@link StaticLayout} by using this text.
- *
- * <pre>
- * Example of background measurement.
- * <code>
- *  void asyncSetText(final TextView textView, final String expensiveLongString, Handler handler) {
- *      final PrecomputedText.Params params = textView.getTextParams();
- *      handler.post(() -> {
- *          final PrecomputedText precomputedText
- *                  = PrecomputedText.create(expensiveLongString, params);
- *          textView.post(() -> {
- *              textView.setPrecomputedTextOrThrow(precomputedText);
- *          });
- *      });
- *  }
- * </code>
- * </pre>
- *
- * Note that the {@link PrecomputedText} created from different parameters of the target
- * {@link android.widget.TextView} will be rejected internally and compute the text layout again
- * with the current {@link android.widget.TextView} parameters.
- */
-public class PrecomputedText {
-    private static final char LINE_FEED = '\n';
-
-    /**
-     * The information required for building {@link PrecomputedText}.
-     *
-     * Contains information required for precomputing text measurement metadata, so it can be done
-     * in isolation of a {@link android.widget.TextView} or {@link StaticLayout}, when final layout
-     * constraints are not known.
-     */
-    public static class Params {
-        // The TextPaint used for measurement.
-        private final @NonNull TextPaint mPaint;
-
-        // The requested text direction.
-        private final @NonNull TextDirectionHeuristic mTextDir;
-
-        // The break strategy for this measured text.
-        private final @Layout.BreakStrategy int mBreakStrategy;
-
-        // The hyphenation frequency for this measured text.
-        private final @Layout.HyphenationFrequency int mHyphenationFrequency;
-
-        /**
-         * A builder for creating {@link Params}.
-         */
-        public static class Builder {
-            // The TextPaint used for measurement.
-            private final @NonNull TextPaint mPaint;
-
-            // The requested text direction.
-            private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
-
-            // The break strategy for this measured text.
-            private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
-
-            // The hyphenation frequency for this measured text.
-            private @Layout.HyphenationFrequency int mHyphenationFrequency =
-                    Layout.HYPHENATION_FREQUENCY_NORMAL;
-
-            /**
-             * Builder constructor
-             *
-             * @param paint The paint to be used for drawing
-             */
-            public Builder(@NonNull TextPaint paint) {
-                mPaint = paint;
-            }
-
-            /**
-             * Set the line break strategy
-             *
-             * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}.
-             *
-             * @param strategy The break strategy
-             * @return this builder, useful for chaining
-             */
-            public Builder setBreakStrategy(@Layout.BreakStrategy int strategy) {
-                mBreakStrategy = strategy;
-                return this;
-            }
-
-            /**
-             * Set the hyphenation frequency
-             *
-             * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
-             *
-             * @param frequency The hyphenation frequency
-             * @return this builder, useful for chaining
-             */
-            public Builder setHyphenationFrequency(@Layout.HyphenationFrequency int frequency) {
-                mHyphenationFrequency = frequency;
-                return this;
-            }
-
-            /**
-             * Set the text direction heuristic
-             *
-             * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
-             *
-             * @param textDir The text direction heuristic for resolving bidi behavior
-             * @return this builder, useful for chaining
-             */
-            public Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) {
-                mTextDir = textDir;
-                return this;
-            }
-
-            /**
-             * Build the {@link Params}
-             *
-             * @return the layout parameter
-             */
-            public @NonNull Params build() {
-                return new Params(mPaint, mTextDir, mBreakStrategy, mHyphenationFrequency);
-            }
-        }
-
-        // Use Builder instead.
-        /** @hide */
-        public Params(@NonNull TextPaint paint, @NonNull TextDirectionHeuristic textDir,
-                @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) {
-            mPaint = paint;
-            mTextDir = textDir;
-            mBreakStrategy = strategy;
-            mHyphenationFrequency = frequency;
-        }
-
-        /**
-         * Returns the {@link TextPaint} for this text
-         *
-         * @return A {@link TextPaint}
-         */
-        public @NonNull TextPaint getTextPaint() {
-            return mPaint;
-        }
-
-        /**
-         * Returns the {@link TextDirectionHeuristic} for this text
-         *
-         * @return A {@link TextDirectionHeuristic}
-         */
-        public @NonNull TextDirectionHeuristic getTextDirection() {
-            return mTextDir;
-        }
-
-        /**
-         * Returns the break strategy for this text
-         *
-         * @return A line break strategy
-         */
-        public @Layout.BreakStrategy int getBreakStrategy() {
-            return mBreakStrategy;
-        }
-
-        /**
-         * Returns the hyphenation frequency for this text
-         *
-         * @return A hyphenation frequency
-         */
-        public @Layout.HyphenationFrequency int getHyphenationFrequency() {
-            return mHyphenationFrequency;
-        }
-
-        private boolean sameTextMetricsInternal(@NonNull TextPaint paint,
-                @NonNull TextDirectionHeuristic textDir, @Layout.BreakStrategy int strategy,
-                @Layout.HyphenationFrequency int frequency) {
-            return mTextDir == textDir
-                && mBreakStrategy == strategy
-                && mHyphenationFrequency == frequency
-                && mPaint.equalsForTextMeasurement(paint);
-        }
-
-        /**
-         * Check if the same text layout.
-         *
-         * @return true if this and the given param result in the same text layout
-         */
-        public boolean sameTextMetrics(@NonNull Params param) {
-            return sameTextMetricsInternal(param.mPaint, param.mTextDir, param.mBreakStrategy,
-                    param.mHyphenationFrequency);
-        }
-    };
-
-    // The original text.
-    private final @NonNull CharSequence mText;
-
-    // The inclusive start offset of the measuring target.
-    private final @IntRange(from = 0) int mStart;
-
-    // The exclusive end offset of the measuring target.
-    private final @IntRange(from = 0) int mEnd;
-
-    private final @NonNull Params mParams;
-
-    // The measured paragraph texts.
-    private final @NonNull MeasuredParagraph[] mMeasuredParagraphs;
-
-    // The sorted paragraph end offsets.
-    private final @NonNull int[] mParagraphBreakPoints;
-
-    /**
-     * Create a new {@link PrecomputedText} which will pre-compute text measurement and glyph
-     * positioning information.
-     * <p>
-     * This can be expensive, so computing this on a background thread before your text will be
-     * presented can save work on the UI thread.
-     * </p>
-     *
-     * @param text The text to be measured
-     * @param param Parameters that define how text will be precomputed
-     * @return A {@link PrecomputedText}
-     */
-    public static PrecomputedText create(@NonNull CharSequence text, @NonNull Params param) {
-        return createInternal(text, param, 0, text.length(), true /* compute full Layout */);
-    }
-
-
-    /** @hide */
-    public static PrecomputedText createWidthOnly(@NonNull CharSequence text, @NonNull Params param,
-            @IntRange(from = 0) int start, @IntRange(from = 0) int end) {
-        return createInternal(text, param, start, end, false /* compute width only */);
-    }
-
-    private static PrecomputedText createInternal(@NonNull CharSequence text, @NonNull Params param,
-            @IntRange(from = 0) int start, @IntRange(from = 0) int end, boolean computeLayout) {
-        Preconditions.checkNotNull(text);
-        Preconditions.checkNotNull(param);
-        final boolean needHyphenation = param.getBreakStrategy() != Layout.BREAK_STRATEGY_SIMPLE
-                && param.getHyphenationFrequency() != Layout.HYPHENATION_FREQUENCY_NONE;
-
-        final IntArray paragraphEnds = new IntArray();
-        final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();
-
-        int paraEnd = 0;
-        for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
-            paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end);
-            if (paraEnd < 0) {
-                // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph
-                // end.
-                paraEnd = end;
-            } else {
-                paraEnd++;  // Includes LINE_FEED(U+000A) to the prev paragraph.
-            }
-
-            paragraphEnds.add(paraEnd);
-            measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
-                    param.getTextPaint(), text, paraStart, paraEnd, param.getTextDirection(),
-                    needHyphenation, computeLayout, null /* no recycle */));
-        }
-
-        return new PrecomputedText(text, start, end, param,
-                                measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]),
-                                paragraphEnds.toArray());
-    }
-
-    // Use PrecomputedText.create instead.
-    private PrecomputedText(@NonNull CharSequence text, @IntRange(from = 0) int start,
-            @IntRange(from = 0) int end, @NonNull Params param,
-            @NonNull MeasuredParagraph[] measuredTexts, @NonNull int[] paragraphBreakPoints) {
-        mText = TextUtils.stringOrSpannedString(text);
-        mStart = start;
-        mEnd = end;
-        mParams = param;
-        mMeasuredParagraphs = measuredTexts;
-        mParagraphBreakPoints = paragraphBreakPoints;
-    }
-
-    /**
-     * Return the underlying text.
-     */
-    public @NonNull CharSequence getText() {
-        return mText;
-    }
-
-    /**
-     * Returns the inclusive start offset of measured region.
-     * @hide
-     */
-    public @IntRange(from = 0) int getStart() {
-        return mStart;
-    }
-
-    /**
-     * Returns the exclusive end offset of measured region.
-     * @hide
-     */
-    public @IntRange(from = 0) int getEnd() {
-        return mEnd;
-    }
-
-    /**
-     * Returns the layout parameters used to measure this text.
-     */
-    public @NonNull Params getParams() {
-        return mParams;
-    }
-
-    /**
-     * Returns the length of the paragraph of this text.
-     */
-    public @IntRange(from = 0) int getParagraphCount() {
-        return mParagraphBreakPoints.length;
-    }
-
-    /**
-     * Returns the paragraph start offset of the text.
-     */
-    public @IntRange(from = 0) int getParagraphStart(@IntRange(from = 0) int paraIndex) {
-        Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
-        return paraIndex == 0 ? mStart : mParagraphBreakPoints[paraIndex - 1];
-    }
-
-    /**
-     * Returns the paragraph end offset of the text.
-     */
-    public @IntRange(from = 0) int getParagraphEnd(@IntRange(from = 0) int paraIndex) {
-        Preconditions.checkArgumentInRange(paraIndex, 0, getParagraphCount(), "paraIndex");
-        return mParagraphBreakPoints[paraIndex];
-    }
-
-    /** @hide */
-    public @NonNull MeasuredParagraph getMeasuredParagraph(@IntRange(from = 0) int paraIndex) {
-        return mMeasuredParagraphs[paraIndex];
-    }
-
-    /**
-     * Returns true if the given TextPaint gives the same result of text layout for this text.
-     * @hide
-     */
-    public boolean canUseMeasuredResult(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
-            @NonNull TextDirectionHeuristic textDir, @NonNull TextPaint paint,
-            @Layout.BreakStrategy int strategy, @Layout.HyphenationFrequency int frequency) {
-        final TextPaint mtPaint = mParams.getTextPaint();
-        return mStart == start
-            && mEnd == end
-            && mParams.sameTextMetricsInternal(paint, textDir, strategy, frequency);
-    }
-
-    /** @hide */
-    public int findParaIndex(@IntRange(from = 0) int pos) {
-        // TODO: Maybe good to remove paragraph concept from PrecomputedText and add substring
-        //       layout support to StaticLayout.
-        for (int i = 0; i < mParagraphBreakPoints.length; ++i) {
-            if (pos < mParagraphBreakPoints[i]) {
-                return i;
-            }
-        }
-        throw new IndexOutOfBoundsException(
-            "pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1]
-            + ", gave " + pos);
-    }
-
-    /** @hide */
-    public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
-        final int paraIndex = findParaIndex(start);
-        final int paraStart = getParagraphStart(paraIndex);
-        final int paraEnd = getParagraphEnd(paraIndex);
-        if (start < paraStart || paraEnd < end) {
-            throw new RuntimeException("Cannot measured across the paragraph:"
-                + "para: (" + paraStart + ", " + paraEnd + "), "
-                + "request: (" + start + ", " + end + ")");
-        }
-        return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
-    }
-
-    /**
-     * Returns the size of native PrecomputedText memory usage
-     *
-     * Note that this is not guaranteed to be accurate. Must be used only for testing purposes.
-     * @hide
-     */
-    public int getMemoryUsage() {
-        int r = 0;
-        for (int i = 0; i < getParagraphCount(); ++i) {
-            r += getMeasuredParagraph(i).getMemoryUsage();
-        }
-        return r;
-    }
-}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 5869802..e62f421 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -78,23 +78,6 @@
         /**
          * Obtain a builder for constructing StaticLayout objects.
          *
-         * @param source The precomputed text.
-         * @param start The index of the start of the text
-         * @param end The index + 1 of the end of the text
-         * @param paint The base paint used for layout
-         * @param width The width in pixels
-         * @return a builder object used for constructing the StaticLayout
-         */
-        @NonNull
-        public static Builder obtain(@NonNull PrecomputedText source, @IntRange(from = 0) int start,
-                @IntRange(from = 0) int end, @NonNull TextPaint paint,
-                @IntRange(from = 0) int width) {
-            return obtain(source.getText(), source, start, end, paint, width);
-        }
-
-        /**
-         * Obtain a builder for constructing StaticLayout objects.
-         *
          * @param source The text to be laid out, optionally with spans
          * @param start The index of the start of the text
          * @param end The index + 1 of the end of the text
@@ -106,12 +89,6 @@
         public static Builder obtain(@NonNull CharSequence source, @IntRange(from = 0) int start,
                 @IntRange(from = 0) int end, @NonNull TextPaint paint,
                 @IntRange(from = 0) int width) {
-            return obtain(source, null, start, end, paint, width);
-        }
-
-        private static Builder obtain(@NonNull CharSequence source, @Nullable PrecomputedText text,
-                @IntRange(from = 0) int start, @IntRange(from = 0) int end,
-                @NonNull TextPaint paint, @IntRange(from = 0) int width) {
             Builder b = sPool.acquire();
             if (b == null) {
                 b = new Builder();
@@ -119,7 +96,6 @@
 
             // set default initial values
             b.mText = source;
-            b.mPrecomputed = text;
             b.mStart = start;
             b.mEnd = end;
             b.mPaint = paint;
@@ -452,7 +428,6 @@
         }
 
         private CharSequence mText;
-        private PrecomputedText mPrecomputed;
         private int mStart;
         private int mEnd;
         private TextPaint mPaint;
@@ -515,7 +490,7 @@
             float spacingmult, float spacingadd,
             boolean includepad,
             TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
-        this(source, null /* precomputed */, bufstart, bufend, paint, outerwidth, align,
+        this(source, bufstart, bufend, paint, outerwidth, align,
                 TextDirectionHeuristics.FIRSTSTRONG_LTR,
                 spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth, Integer.MAX_VALUE);
     }
@@ -525,16 +500,7 @@
      * @deprecated Use {@link Builder} instead.
      */
     @Deprecated
-    public StaticLayout(CharSequence source, int bufstart, int bufend, TextPaint paint,
-                        int outerwidth, Alignment align, TextDirectionHeuristic textDir,
-                        float spacingmult, float spacingadd, boolean includepad,
-                        TextUtils.TruncateAt ellipsize, int ellipsizedWidth, int maxLines) {
-        this(source, null /* precomputed */, bufstart, bufend, paint, outerwidth, align, textDir,
-                spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth, maxLines);
-    }
-
-    /** @hide */
-    public StaticLayout(CharSequence source, PrecomputedText precomputed, int bufstart, int bufend,
+    public StaticLayout(CharSequence source, int bufstart, int bufend,
                         TextPaint paint, int outerwidth,
                         Alignment align, TextDirectionHeuristic textDir,
                         float spacingmult, float spacingadd,
@@ -545,7 +511,6 @@
                 : (source instanceof Spanned)
                     ? new SpannedEllipsizer(source)
                     : new Ellipsizer(source),
-              (ellipsize == null) ? precomputed : null,
               paint, outerwidth, align, textDir, spacingmult, spacingadd);
 
         Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
@@ -686,20 +651,43 @@
                 b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
                 indents, mLeftPaddings, mRightPaddings);
 
-        PrecomputedText measured = null;
-        final Spanned spanned = (b.mText instanceof Spanned) ? (Spanned) b.mText : null;
-        if (b.mPrecomputed != null) {
-            if (b.mPrecomputed.canUseMeasuredResult(bufStart, bufEnd, textDir, paint,
-                      b.mBreakStrategy, b.mHyphenationFrequency)) {
-                measured = b.mPrecomputed;
+        MeasuredText measured = null;
+        final Spanned spanned;
+        final boolean canUseMeasuredText;
+        if (source instanceof MeasuredText) {
+            measured = (MeasuredText) source;
+
+            if (bufStart != measured.getStart() || bufEnd != measured.getEnd()) {
+                // The buffer position has changed. Re-measure here.
+                canUseMeasuredText = false;
+            } else if (b.mBreakStrategy != measured.getBreakStrategy()
+                    || b.mHyphenationFrequency != measured.getHyphenationFrequency()) {
+                // The computed hyphenation pieces may not be able to used. Re-measure it.
+                canUseMeasuredText = false;
+            } else {
+                // We can use measured information.
+                canUseMeasuredText = true;
             }
-        }
-        if (measured == null) {
-            final PrecomputedText.Params param = new PrecomputedText.Params(paint, textDir,
-                    b.mBreakStrategy, b.mHyphenationFrequency);
-            measured = PrecomputedText.createWidthOnly(source, param, bufStart, bufEnd);
+        } else {
+            canUseMeasuredText = false;
         }
 
+        if (!canUseMeasuredText) {
+            measured = new MeasuredText.Builder(source, paint)
+                    .setRange(bufStart, bufEnd)
+                    .setTextDirection(textDir)
+                    .setBreakStrategy(b.mBreakStrategy)
+                    .setHyphenationFrequency(b.mHyphenationFrequency)
+                    .build(false /* full layout is not necessary for line breaking */);
+            spanned = (source instanceof Spanned) ? (Spanned) source : null;
+        } else {
+            final CharSequence original = measured.getText();
+            spanned = (original instanceof Spanned) ? (Spanned) original : null;
+            // Overwrite with the one when measured.
+            // TODO: Give an option for developer not to overwrite and measure again here?
+            textDir = measured.getTextDir();
+            paint = measured.getPaint();
+        }
 
         try {
             for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) {
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index be5bb4d..55367dc 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -16,7 +16,6 @@
 
 package android.text;
 
-import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Canvas;
@@ -61,7 +60,7 @@
     private char[] mChars;
     private boolean mCharsValid;
     private Spanned mSpanned;
-    private PrecomputedText mComputed;
+    private MeasuredText mMeasured;
 
     // Additional width of whitespace for justification. This value is per whitespace, thus
     // the line width will increase by mAddedWidth x (number of stretchable whitespaces).
@@ -120,7 +119,7 @@
         tl.mSpanned = null;
         tl.mTabs = null;
         tl.mChars = null;
-        tl.mComputed = null;
+        tl.mMeasured = null;
 
         tl.mMetricAffectingSpanSpanSet.recycle();
         tl.mCharacterStyleSpanSet.recycle();
@@ -150,31 +149,10 @@
      * @param tabStops the tabStops. Can be null.
      */
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public void set(TextPaint paint, CharSequence text, int start,
-            int limit, int dir, Directions directions, boolean hasTabs, TabStops tabStops) {
-        set(paint, text, null, start, limit, dir, directions, hasTabs, tabStops);
-    }
-
-    /**
-     * Initializes a TextLine and prepares it for use.
-     *
-     * @param paint the base paint for the line
-     * @param text the text, can be Styled
-     * @param precomputed the precomputed text
-     * @param start the start of the line relative to the text
-     * @param limit the limit of the line relative to the text
-     * @param dir the paragraph direction of this line
-     * @param directions the directions information of this line
-     * @param hasTabs true if the line might contain tabs
-     * @param tabStops the tabStops.
-     */
-    public void set(@NonNull TextPaint paint, @NonNull CharSequence text,
-            @Nullable PrecomputedText precomputed, @IntRange(from = 0) int start,
-            @IntRange(from = 0) int limit, int dir, @NonNull Directions directions, boolean hasTabs,
-            @Nullable TabStops tabStops) {
+    public void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
+            Directions directions, boolean hasTabs, TabStops tabStops) {
         mPaint = paint;
         mText = text;
-        mComputed = precomputed;
         mStart = start;
         mLen = limit - start;
         mDir = dir;
@@ -192,6 +170,14 @@
             hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0;
         }
 
+        mMeasured = null;
+        if (text instanceof MeasuredText) {
+            MeasuredText mt = (MeasuredText) text;
+            if (mt.canUseMeasuredResult(paint)) {
+                mMeasured = mt;
+            }
+        }
+
         mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
 
         if (mCharsValid) {
@@ -760,12 +746,12 @@
             return wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, runIsRtl, offset);
         } else {
             final int delta = mStart;
-            if (mComputed == null) {
+            if (mMeasured == null) {
                 // TODO: Enable measured getRunAdvance for ReplacementSpan and RTL text.
                 return wp.getRunAdvance(mText, delta + start, delta + end,
                         delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
             } else {
-                return mComputed.getWidth(start + delta, end + delta);
+                return mMeasured.getWidth(start + delta, end + delta);
             }
         }
     }
diff --git a/core/java/android/util/ExceptionUtils.java b/core/java/android/util/ExceptionUtils.java
index da7387f..1a397b3 100644
--- a/core/java/android/util/ExceptionUtils.java
+++ b/core/java/android/util/ExceptionUtils.java
@@ -86,4 +86,16 @@
         while (t.getCause() != null) t = t.getCause();
         return t;
     }
-}
+
+    /**
+     * Appends {@code cause} at the end of the causal chain of {@code t}
+     *
+     * @return {@code t} for convenience
+     */
+    public static @NonNull Throwable appendCause(@NonNull Throwable t, @Nullable Throwable cause) {
+        if (cause != null) {
+            getRootCause(t).initCause(cause);
+        }
+        return t;
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/util/OWNERS b/core/java/android/util/OWNERS
new file mode 100644
index 0000000..86ed122
--- /dev/null
+++ b/core/java/android/util/OWNERS
@@ -0,0 +1,2 @@
+per-file FeatureFlagUtils.java = sbasi@google.com
+per-file FeatureFlagUtils.java = zhfan@google.com
diff --git a/core/java/android/view/RecordingCanvas.java b/core/java/android/view/RecordingCanvas.java
index fc7d828..fbb862b 100644
--- a/core/java/android/view/RecordingCanvas.java
+++ b/core/java/android/view/RecordingCanvas.java
@@ -34,7 +34,7 @@
 import android.graphics.RectF;
 import android.graphics.TemporaryBuffer;
 import android.text.GraphicsOperations;
-import android.text.PrecomputedText;
+import android.text.MeasuredText;
 import android.text.SpannableString;
 import android.text.SpannedString;
 import android.text.TextUtils;
@@ -507,8 +507,8 @@
             TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
             long measuredTextPtr = 0;
             int measuredTextOffset = 0;
-            if (text instanceof PrecomputedText) {
-                PrecomputedText mt = (PrecomputedText) text;
+            if (text instanceof MeasuredText) {
+                MeasuredText mt = (MeasuredText) text;
                 int paraIndex = mt.findParaIndex(start);
                 if (end <= mt.getParagraphEnd(paraIndex)) {
                     // Only support if the target is in the same paragraph.
@@ -641,7 +641,7 @@
     @FastNative
     private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
             int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
-            long nativePrecomputedText, int measuredTextOffset);
+            long nativeMeasuredText, int measuredTextOffset);
 
     @FastNative
     private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index c4a7160..d26a2f6 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -158,7 +158,7 @@
     }
 
     private void applyInterpolator() {
-        if (mInterpolator == null) return;
+        if (mInterpolator == null || mNativePtr == null) return;
 
         long ni;
         if (isNativeInterpolator(mInterpolator)) {
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 990fbdb..29f8442 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -18,6 +18,7 @@
 
 import android.annotation.AnimRes;
 import android.annotation.InterpolatorRes;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
@@ -58,14 +59,43 @@
         }
     };
 
-    /** @hide */
+    /**
+     * Locks AnimationUtils{@link #currentAnimationTimeMillis()} to a fixed value for the current
+     * thread. This is used by {@link android.view.Choreographer} to ensure that all accesses
+     * during a vsync update are synchronized to the timestamp of the vsync.
+     *
+     * It is also exposed to tests to allow for rapid, flake-free headless testing.
+     *
+     * Must be followed by a call to {@link #unlockAnimationClock()} to allow time to
+     * progress. Failing to do this will result in stuck animations, scrolls, and flings.
+     *
+     * Note that time is not allowed to "rewind" and must perpetually flow forward. So the
+     * lock may fail if the time is in the past from a previously returned value, however
+     * time will be frozen for the duration of the lock. The clock is a thread-local, so
+     * ensure that {@link #lockAnimationClock(long)}, {@link #unlockAnimationClock()}, and
+     * {@link #currentAnimationTimeMillis()} are all called on the same thread.
+     *
+     * This is also not reference counted in any way. Any call to {@link #unlockAnimationClock()}
+     * will unlock the clock for everyone on the same thread. It is therefore recommended
+     * for tests to use their own thread to ensure that there is no collision with any existing
+     * {@link android.view.Choreographer} instance.
+     *
+     * @hide
+     * */
+    @TestApi
     public static void lockAnimationClock(long vsyncMillis) {
         AnimationState state = sAnimationState.get();
         state.animationClockLocked = true;
         state.currentVsyncTimeMillis = vsyncMillis;
     }
 
-    /** @hide */
+    /**
+     * Frees the time lock set in place by {@link #lockAnimationClock(long)}. Must be called
+     * to allow the animation clock to self-update.
+     *
+     * @hide
+     */
+    @TestApi
     public static void unlockAnimationClock() {
         sAnimationState.get().animationClockLocked = false;
     }
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 41d05a5..cf81b97 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -356,6 +356,13 @@
     @GuardedBy("mLock")
     @Nullable private ArraySet<AutofillId> mFillableIds;
 
+    /**
+     * Views that were already "entered" - if they're entered again when the session is not active,
+     * they're ignored
+     * */
+    @GuardedBy("mLock")
+    @Nullable private ArraySet<AutofillId> mEnteredIds;
+
     /** If set, session is commited when the field is clicked. */
     @GuardedBy("mLock")
     @Nullable private AutofillId mSaveTriggerId;
@@ -711,17 +718,29 @@
     }
 
     @GuardedBy("mLock")
-    private boolean shouldIgnoreViewEnteredLocked(@NonNull View view, int flags) {
+    private boolean shouldIgnoreViewEnteredLocked(@NonNull AutofillId id, int flags) {
         if (isDisabledByServiceLocked()) {
             if (sVerbose) {
-                Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + view
-                        + ") on state " + getStateAsStringLocked());
+                Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
+                        + ") on state " + getStateAsStringLocked() + " because disabled by svc");
             }
             return true;
         }
-        if (sVerbose && isFinishedLocked()) {
-            Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + view
-                    + ") on state " + getStateAsStringLocked());
+        if (isFinishedLocked()) {
+            // Session already finished: ignore if automatic request and view already entered
+            if ((flags & FLAG_MANUAL_REQUEST) == 0 && mEnteredIds != null
+                    && mEnteredIds.contains(id)) {
+                if (sVerbose) {
+                    Log.v(TAG, "ignoring notifyViewEntered(flags=" + flags + ", view=" + id
+                            + ") on state " + getStateAsStringLocked()
+                            + " because view was already entered: " + mEnteredIds);
+                }
+                return true;
+            }
+        }
+        if (sVerbose) {
+            Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id
+                    + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds);
         }
         return false;
     }
@@ -753,7 +772,8 @@
     /** Returns AutofillCallback if need fire EVENT_INPUT_UNAVAILABLE */
     @GuardedBy("mLock")
     private AutofillCallback notifyViewEnteredLocked(@NonNull View view, int flags) {
-        if (shouldIgnoreViewEnteredLocked(view, flags)) return null;
+        final AutofillId id = getAutofillId(view);
+        if (shouldIgnoreViewEnteredLocked(id, flags)) return null;
 
         AutofillCallback callback = null;
 
@@ -766,7 +786,6 @@
         } else {
             // don't notify entered when Activity is already in background
             if (!isClientDisablingEnterExitEvent()) {
-                final AutofillId id = getAutofillId(view);
                 final AutofillValue value = view.getAutofillValue();
 
                 if (!isActiveLocked()) {
@@ -776,6 +795,7 @@
                     // Update focus on existing session.
                     updateSessionLocked(id, null, value, ACTION_VIEW_ENTERED, flags);
                 }
+                addEnteredIdLocked(id);
             }
         }
         return callback;
@@ -900,8 +920,9 @@
     @GuardedBy("mLock")
     private AutofillCallback notifyViewEnteredLocked(View view, int virtualId, Rect bounds,
                                                      int flags) {
+        final AutofillId id = getAutofillId(view, virtualId);
         AutofillCallback callback = null;
-        if (shouldIgnoreViewEnteredLocked(view, flags)) return callback;
+        if (shouldIgnoreViewEnteredLocked(id, flags)) return callback;
 
         ensureServiceClientAddedIfNeededLocked();
 
@@ -912,8 +933,6 @@
         } else {
             // don't notify entered when Activity is already in background
             if (!isClientDisablingEnterExitEvent()) {
-                final AutofillId id = getAutofillId(view, virtualId);
-
                 if (!isActiveLocked()) {
                     // Starts new session.
                     startSessionLocked(id, bounds, null, flags);
@@ -921,11 +940,20 @@
                     // Update focus on existing session.
                     updateSessionLocked(id, bounds, null, ACTION_VIEW_ENTERED, flags);
                 }
+                addEnteredIdLocked(id);
             }
         }
         return callback;
     }
 
+    @GuardedBy("mLock")
+    private void addEnteredIdLocked(@NonNull AutofillId id) {
+        if (mEnteredIds == null) {
+            mEnteredIds = new ArraySet<>(1);
+        }
+        mEnteredIds.add(id);
+    }
+
     /**
      * Called when a virtual view that supports autofill is exited.
      *
@@ -992,9 +1020,9 @@
             }
 
             if (!mEnabled || !isActiveLocked()) {
-                if (sVerbose && mEnabled) {
-                    Log.v(TAG, "notifyValueChanged(" + view + "): ignoring on state "
-                            + getStateAsStringLocked());
+                if (sVerbose) {
+                    Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
+                            + "): ignoring on state " + getStateAsStringLocked());
                 }
                 return;
             }
@@ -1024,6 +1052,10 @@
         }
         synchronized (mLock) {
             if (!mEnabled || !isActiveLocked()) {
+                if (sVerbose) {
+                    Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId
+                            + "): ignoring on state " + getStateAsStringLocked());
+                }
                 return;
             }
 
@@ -1032,7 +1064,6 @@
         }
     }
 
-
     /**
      * Called when a {@link View} is clicked. Currently only used by views that should trigger save.
      *
@@ -1392,7 +1423,8 @@
         if (sVerbose) {
             Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value
                     + ", flags=" + flags + ", state=" + getStateAsStringLocked()
-                    + ", compatMode=" + isCompatibilityModeEnabledLocked());
+                    + ", compatMode=" + isCompatibilityModeEnabledLocked()
+                    + ", enteredIds=" + mEnteredIds);
         }
         if (mState != STATE_UNKNOWN && !isFinishedLocked() && (flags & FLAG_MANUAL_REQUEST) == 0) {
             if (sVerbose) {
@@ -1430,7 +1462,7 @@
             throw e.rethrowFromSystemServer();
         }
 
-        resetSessionLocked();
+        resetSessionLocked(/* resetEnteredIds= */ true);
     }
 
     @GuardedBy("mLock")
@@ -1445,22 +1477,25 @@
             throw e.rethrowFromSystemServer();
         }
 
-        resetSessionLocked();
+        resetSessionLocked(/* resetEnteredIds= */ true);
     }
 
     @GuardedBy("mLock")
-    private void resetSessionLocked() {
+    private void resetSessionLocked(boolean resetEnteredIds) {
         mSessionId = NO_SESSION;
         mState = STATE_UNKNOWN;
         mTrackedViews = null;
         mFillableIds = null;
         mSaveTriggerId = null;
+        if (resetEnteredIds) {
+            mEnteredIds = null;
+        }
     }
 
     @GuardedBy("mLock")
     private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int action,
             int flags) {
-        if (sVerbose && action != ACTION_VIEW_EXITED) {
+        if (sVerbose) {
             Log.v(TAG, "updateSessionLocked(): id=" + id + ", bounds=" + bounds
                     + ", value=" + value + ", action=" + action + ", flags=" + flags);
         }
@@ -1630,7 +1665,7 @@
             mEnabled = (flags & SET_STATE_FLAG_ENABLED) != 0;
             if (!mEnabled || (flags & SET_STATE_FLAG_RESET_SESSION) != 0) {
                 // Reset the session state
-                resetSessionLocked();
+                resetSessionLocked(/* resetEnteredIds= */ true);
             }
             if ((flags & SET_STATE_FLAG_RESET_CLIENT) != 0) {
                 // Reset connection to system
@@ -1826,7 +1861,7 @@
     private void setSessionFinished(int newState) {
         synchronized (mLock) {
             if (sVerbose) Log.v(TAG, "setSessionFinished(): from " + mState + " to " + newState);
-            resetSessionLocked();
+            resetSessionLocked(/* resetEnteredIds= */ false);
             mState = newState;
         }
     }
@@ -1954,6 +1989,7 @@
             pw.print(pfx2); pw.print("invisible:"); pw.println(mTrackedViews.mInvisibleTrackedIds);
         }
         pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds);
+        pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds);
         pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId);
         pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish);
         pw.print(pfx); pw.print("compat mode enabled: "); pw.println(
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f6e771a..5710db3 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -80,8 +80,8 @@
 import android.text.InputFilter;
 import android.text.InputType;
 import android.text.Layout;
+import android.text.MeasuredText;
 import android.text.ParcelableSpan;
-import android.text.PrecomputedText;
 import android.text.Selection;
 import android.text.SpanWatcher;
 import android.text.Spannable;
@@ -637,7 +637,6 @@
     private CharSequence mText;
     private CharSequence mTransformed;
     private BufferType mBufferType = BufferType.NORMAL;
-    private PrecomputedText mPrecomputed;
 
     private CharSequence mHint;
     private Layout mHintLayout;
@@ -4086,80 +4085,6 @@
     }
 
     /**
-     * Gets the parameters for text layout precomputation, for use with {@link PrecomputedText}
-     *
-     * @return A current {@link PrecomputedText.Params}
-     */
-    public @NonNull PrecomputedText.Params getTextMetricsParams() {
-        return new PrecomputedText.Params(new TextPaint(mTextPaint), getTextDirectionHeuristic(),
-                mBreakStrategy, mHyphenationFrequency);
-    }
-
-    /**
-     * Apply the text layout parameter.
-     */
-    public void setTextMetricsParams(@NonNull PrecomputedText.Params params) {
-        mTextPaint.set(params.getTextPaint());
-        mTextDir = params.getTextDirection();
-        mBreakStrategy = params.getBreakStrategy();
-        mHyphenationFrequency = params.getHyphenationFrequency();
-        if (mLayout != null) {
-            nullLayouts();
-            requestLayout();
-            invalidate();
-        }
-    }
-
-    /**
-     * Sets the precomputed text.
-     *
-     * If the parameters for the precomputed text is different from current text view parameters,
-     * apply the parameteres to the text view too.
-     *
-     * @param text A precomputed text.
-     */
-    public void setPrecomputedTextAndParams(@NonNull PrecomputedText text) {
-        Preconditions.checkNotNull(text);
-        final PrecomputedText.Params params = text.getParams();
-        if (!params.sameTextMetrics(getTextMetricsParams())) {
-            setTextMetricsParams(params);
-        }
-        setText(text.getText());
-        if (mTransformed != text.getText()) {
-            // setText modified given text for some reasons, selection, transformation, etc.
-            // Can't use computed result.
-            return;
-        } else {
-            mPrecomputed = text;
-        }
-    }
-
-    /**
-     * Sets the precomputed text.
-     *
-     * If the parameters for the precomputed text is different from current text view parameters,
-     * throws {@link IllegalArgumentException}.
-     *
-     * @param text A precomputed text.
-     */
-    public void setPrecomputedTextOrThrow(@NonNull PrecomputedText text) {
-        Preconditions.checkNotNull(text);
-        final PrecomputedText.Params params = text.getParams();
-        if (!params.sameTextMetrics(getTextMetricsParams())) {
-            throw new IllegalArgumentException(
-                "The precomputed configuration is different from this TextView.");
-        }
-        setText(text.getText());
-        if (mTransformed != text.getText()) {
-            // setText modified given text for some reasons, selection, transformation, etc.
-            // Can't use computed result.
-            // TODO: Do we throw an exception here too?
-        } else {
-            mPrecomputed = text;
-        }
-    }
-
-    /**
      * Set justification mode. The default value is {@link Layout#JUSTIFICATION_MODE_NONE}. If the
      * last line is too short for justification, the last line will be displayed with the
      * alignment set by {@link android.view.View#setTextAlignment}.
@@ -5594,7 +5519,6 @@
 
     private void setText(CharSequence text, BufferType type,
                          boolean notifyBefore, int oldlen) {
-        mPrecomputed = null;
         mTextSetFromXmlOrResourceId = false;
         if (text == null) {
             text = "";
@@ -5653,7 +5577,7 @@
             if (imm != null) imm.restartInput(this);
         } else if (type == BufferType.SPANNABLE || mMovement != null) {
             text = mSpannableFactory.newSpannable(text);
-        } else if (!(text instanceof CharWrapper)) {
+        } else if (!(text instanceof MeasuredText || text instanceof CharWrapper)) {
             text = TextUtils.stringOrSpannedString(text);
         }
 
@@ -8320,8 +8244,7 @@
             result = builder.build();
         } else {
             if (boring == UNKNOWN_BORING) {
-                boring = BoringLayout.isBoring(mTransformed, mPrecomputed, mTextPaint, mTextDir,
-                        mBoring);
+                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
                 if (boring != null) {
                     mBoring = boring;
                 }
@@ -8359,15 +8282,9 @@
             }
         }
         if (result == null) {
-            StaticLayout.Builder builder;
-            if (mPrecomputed != null) {
-                builder = StaticLayout.Builder.obtain(mPrecomputed, 0,
-                        mPrecomputed.getText().length(), mTextPaint, wantWidth);
-            } else {
-                builder = StaticLayout.Builder.obtain(mTransformed, 0, mTransformed.length(),
-                        mTextPaint, wantWidth);
-            }
-            builder.setAlignment(alignment)
+            StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,
+                    0, mTransformed.length(), mTextPaint, wantWidth)
+                    .setAlignment(alignment)
                     .setTextDirection(mTextDir)
                     .setLineSpacing(mSpacingAdd, mSpacingMult)
                     .setIncludePad(mIncludePad)
@@ -8494,8 +8411,7 @@
             }
 
             if (des < 0) {
-                boring = BoringLayout.isBoring(mTransformed, mPrecomputed, mTextPaint, mTextDir,
-                        mBoring);
+                boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
                 if (boring != null) {
                     mBoring = boring;
                 }
@@ -11780,9 +11696,6 @@
     }
 
     /**
-     * Returns the current {@link TextDirectionHeuristic}
-     *
-     * @return A {@link TextDirectionHeuristic}.
      * @hide
      */
     protected TextDirectionHeuristic getTextDirectionHeuristic() {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 6a56f45..4c5991e 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -5286,6 +5286,18 @@
                 case TelephonyManager.NETWORK_TYPE_HSPAP:
                     bin = DATA_CONNECTION_HSPAP;
                     break;
+                case TelephonyManager.NETWORK_TYPE_GSM:
+                    bin = DATA_CONNECTION_GSM;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_TD_SCDMA:
+                    bin = DATA_CONNECTION_TD_SCDMA;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_IWLAN:
+                    bin = DATA_CONNECTION_IWLAN;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_LTE_CA:
+                    bin = DATA_CONNECTION_LTE_CA;
+                    break;
                 default:
                     bin = DATA_CONNECTION_OTHER;
                     break;
diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS
new file mode 100644
index 0000000..21d750c
--- /dev/null
+++ b/core/java/com/android/internal/util/OWNERS
@@ -0,0 +1,24 @@
+per-file AsyncChannel*=lorenzo@google.com
+per-file AsyncChannel*=satk@google.com
+per-file AsyncChannel*=silberst@google.com
+per-file BitUtils*=ek@google.com
+per-file BitUtils*=lorenzo@google.com
+per-file BitUtils*=satk@google.com
+per-file MessageUtils*=ek@google.com
+per-file MessageUtils*=lorenzo@google.com
+per-file MessageUtils*=satk@google.com
+per-file Protocol*=ek@google.com
+per-file Protocol*=lorenzo@google.com
+per-file Protocol*=quiche@google.com
+per-file Protocol*=satk@google.com
+per-file Protocol*=silberst@google.com
+per-file RingBuffer*=ek@google.com
+per-file RingBuffer*=lorenzo@google.com
+per-file RingBuffer*=satk@google.com
+per-file State*=ek@google.com
+per-file State*=lorenzo@google.com
+per-file State*=quiche@google.com
+per-file State*=silberst@google.com
+per-file TokenBucket*=ek@google.com
+per-file TokenBucket*=lorenzo@google.com
+per-file TokenBucket*=satk@google.com
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
new file mode 100644
index 0000000..ce79049
--- /dev/null
+++ b/core/jni/OWNERS
@@ -0,0 +1,12 @@
+# Camera
+per-file *Camera*,*camera* = cychen@google.com
+per-file *Camera*,*camera* = epeev@google.com
+per-file *Camera*,*camera* = etalvala@google.com
+per-file *Camera*,*camera* = shuzhenwang@google.com
+per-file *Camera*,*camera* = yinchiayeh@google.com
+per-file *Camera*,*camera* = zhijunhe@google.com
+
+# Connectivity
+per-file android_net_*=ek@google.com
+per-file android_net_*=lorenzo@google.com
+per-file android_net_*=satk@google.com
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 2c05d0b..482d028 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -1008,23 +1008,6 @@
         return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr);
     }
 
-    static jboolean equalsForTextMeasurement(jlong lPaint, jlong rPaint) {
-        if (lPaint == rPaint) {
-            return true;
-        }
-        Paint* leftPaint = reinterpret_cast<Paint*>(lPaint);
-        Paint* rightPaint = reinterpret_cast<Paint*>(rPaint);
-
-        const Typeface* leftTypeface = Typeface::resolveDefault(leftPaint->getAndroidTypeface());
-        const Typeface* rightTypeface = Typeface::resolveDefault(rightPaint->getAndroidTypeface());
-        minikin::MinikinPaint leftMinikinPaint
-                = MinikinUtils::prepareMinikinPaint(leftPaint, leftTypeface);
-        minikin::MinikinPaint rightMinikinPaint
-                = MinikinUtils::prepareMinikinPaint(rightPaint, rightTypeface);
-
-        return leftMinikinPaint == rightMinikinPaint;
-    }
-
 }; // namespace PaintGlue
 
 static const JNINativeMethod methods[] = {
@@ -1124,8 +1107,7 @@
     {"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition},
     {"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness},
     {"nSetShadowLayer", "(JFFFI)V", (void*)PaintGlue::setShadowLayer},
-    {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer},
-    {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
+    {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer}
 };
 
 int register_android_graphics_Paint(JNIEnv* env) {
diff --git a/core/proto/android/net/OWNERS b/core/proto/android/net/OWNERS
new file mode 100644
index 0000000..a845dcb
--- /dev/null
+++ b/core/proto/android/net/OWNERS
@@ -0,0 +1,5 @@
+set noparent
+
+ek@google.com
+lorenzo@google.com
+satk@google.com
diff --git a/core/proto/android/os/cpufreq.proto b/core/proto/android/os/cpufreq.proto
index 8481ffc..46f4901 100644
--- a/core/proto/android/os/cpufreq.proto
+++ b/core/proto/android/os/cpufreq.proto
@@ -16,32 +16,30 @@
 syntax = "proto2";
 
 option java_multiple_files = true;
-option java_outer_classname = "CpuFreqProto";
 
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 package android.os;
 
 // cpu frequency time from /sys/devices/system/cpu/cpufreq/all_time_in_state
-message CpuFreq {
+message CpuFreqProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
     optional int32 jiffy_hz = 1; // obtain by system config.
 
-    repeated CpuFreqStats cpu_freqs = 2;
-}
-
-// frequency time pre cpu, unit in jiffy, TODO: obtain jiffies.
-message CpuFreqStats {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
-    optional string cpu_name = 1;
-
-    message TimeInState {
+    // frequency time pre cpu, unit in jiffy.
+    message Stats {
         option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
-        optional int32 state_khz = 1;  // cpu frequency
-        optional int64 time_jiffy = 2; // number of jiffies
+        optional string cpu_name = 1;
+
+        message TimeInState {
+            option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+            optional int32 state_khz = 1;  // cpu frequency
+            optional int64 time_jiffy = 2; // number of jiffies
+        }
+        repeated TimeInState times = 2;
     }
-    repeated TimeInState times = 2;
+    repeated Stats cpu_freqs = 2;
 }
diff --git a/core/proto/android/os/cpuinfo.proto b/core/proto/android/os/cpuinfo.proto
index ca43602..ce69fc9 100644
--- a/core/proto/android/os/cpuinfo.proto
+++ b/core/proto/android/os/cpuinfo.proto
@@ -16,7 +16,6 @@
 syntax = "proto2";
 
 option java_multiple_files = true;
-option java_outer_classname = "CpuInfoProto";
 
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
@@ -28,7 +27,7 @@
  *
  * Next Tag: 6
  */
-message CpuInfo {
+message CpuInfoProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
     message TaskStats {
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 698f394..c0950bfc 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -122,32 +122,32 @@
     ];
 
     // Linux services
-    optional Procrank procrank = 2000 [
+    optional ProcrankProto procrank = 2000 [
         (section).type = SECTION_NONE, // disable procrank until figure out permission
         (section).args = "/system/xbin/procrank"
     ];
 
-    optional PageTypeInfo page_type_info = 2001 [
+    optional PageTypeInfoProto page_type_info = 2001 [
         (section).type = SECTION_FILE,
         (section).args = "/proc/pagetypeinfo"
     ];
 
-    optional KernelWakeSources kernel_wake_sources = 2002 [
+    optional KernelWakeSourcesProto kernel_wake_sources = 2002 [
         (section).type = SECTION_FILE,
         (section).args = "/d/wakeup_sources"
     ];
 
-    optional CpuInfo cpu_info = 2003 [
+    optional CpuInfoProto cpu_info = 2003 [
         (section).type = SECTION_COMMAND,
         (section).args = "top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"
     ];
 
-    optional CpuFreq cpu_freq = 2004 [
+    optional CpuFreqProto cpu_freq = 2004 [
         (section).type = SECTION_FILE,
         (section).args = "/sys/devices/system/cpu/cpufreq/all_time_in_state"
     ];
 
-    optional PsDumpProto processes_and_threads = 2005 [
+    optional PsProto processes_and_threads = 2005 [
         (section).type = SECTION_COMMAND,
         (section).args = "ps -A -T -Z -O pri,nice,rtprio,sched,pcy,time"
     ];
diff --git a/core/proto/android/os/kernelwake.proto b/core/proto/android/os/kernelwake.proto
index c296dab..5021a06 100644
--- a/core/proto/android/os/kernelwake.proto
+++ b/core/proto/android/os/kernelwake.proto
@@ -16,41 +16,40 @@
 syntax = "proto2";
 
 option java_multiple_files = true;
-option java_outer_classname = "WakeupSourcesProto";
 
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 package android.os;
 
-message KernelWakeSources {
+message KernelWakeSourcesProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
     // Kernel records of what caused the application processor to wake up
-    repeated WakeupSourceProto wakeup_sources = 1;
-}
+    message WakeupSource {
+        option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
-// Next Tag: 11
-message WakeupSourceProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+        // Name of the event which triggers application processor
+        optional string name = 1;
 
-    // Name of the event which triggers application processor
-    optional string name = 1;
+        optional int32 active_count = 2;
 
-    optional int32 active_count = 2;
+        optional int32 event_count = 3;
 
-    optional int32 event_count = 3;
+        optional int32 wakeup_count = 4;
 
-    optional int32 wakeup_count = 4;
+        optional int32 expire_count = 5;
 
-    optional int32 expire_count = 5;
+        optional int64 active_since = 6;
 
-    optional int64 active_since = 6;
+        optional int64 total_time = 7;
 
-    optional int64 total_time = 7;
+        optional int64 max_time = 8;
 
-    optional int64 max_time = 8;
+        optional int64 last_change = 9;
 
-    optional int64 last_change = 9;
+        optional int64 prevent_suspend_time = 10;
 
-    optional int64 prevent_suspend_time = 10;
+        // Next Tag: 11
+    }
+    repeated WakeupSource wakeup_sources = 1;
 }
diff --git a/core/proto/android/os/pagetypeinfo.proto b/core/proto/android/os/pagetypeinfo.proto
index b8f618b..f5d77d6 100644
--- a/core/proto/android/os/pagetypeinfo.proto
+++ b/core/proto/android/os/pagetypeinfo.proto
@@ -16,7 +16,6 @@
 syntax = "proto2";
 
 option java_multiple_files = true;
-option java_outer_classname = "PageTypeInfoProto";
 
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
@@ -37,49 +36,47 @@
  *
  *  Next tag: 5
  */
-message PageTypeInfo {
+message PageTypeInfoProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
     optional int32 page_block_order = 1;
 
     optional int32 pages_per_block = 2;
 
-    repeated MigrateTypeProto migrate_types = 3;
+    // Next tag: 5
+    message MigrateType {
+        option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
-    repeated BlockProto blocks = 4;
-}
+        optional int32 node = 1;
 
-// Next tag: 5
-message MigrateTypeProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+        optional string zone = 2;
 
-    optional int32 node = 1;
+        optional string type = 3;
 
-    optional string zone = 2;
+        // order level starts from 0 for 4KB to page_block_order defined above, e.g. 10 for 4096KB
+        repeated int32 free_pages_count = 4;
+    }
+    repeated MigrateType migrate_types = 3;
 
-    optional string type = 3;
+    // Next tag: 9
+    message Block {
+        option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
-    // order level starts from 0 for 4KB to page_block_order defined above, e.g. 10 for 4096KB
-    repeated int32 free_pages_count = 4;
-}
+        optional int32 node = 1;
 
-// Next tag: 9
-message BlockProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+        optional string zone = 2;
 
-    optional int32 node = 1;
+        optional int32 unmovable = 3;
 
-    optional string zone = 2;
+        optional int32 reclaimable = 4;
 
-    optional int32 unmovable = 3;
+        optional int32 movable = 5;
 
-    optional int32 reclaimable = 4;
+        optional int32 cma = 6;
 
-    optional int32 movable = 5;
+        optional int32 reserve = 7;
 
-    optional int32 cma = 6;
-
-    optional int32 reserve = 7;
-
-    optional int32 isolate = 8;
+        optional int32 isolate = 8;
+    }
+    repeated Block blocks = 4;
 }
diff --git a/core/proto/android/os/procrank.proto b/core/proto/android/os/procrank.proto
index 204a5af..ff7515e 100644
--- a/core/proto/android/os/procrank.proto
+++ b/core/proto/android/os/procrank.proto
@@ -16,78 +16,73 @@
 
 syntax = "proto2";
 option java_multiple_files = true;
-option java_outer_classname = "ProcrankProto";
 
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 package android.os;
 
 //Memory usage of running processes
-message Procrank {
+message ProcrankProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
     // Currently running process
-    repeated ProcessProto processes = 1;
+    // Next Tag: 11
+    message Process {
+        option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+        // ID of the process
+        optional int32 pid = 1;
+
+        // virtual set size, unit KB
+        optional int64 vss = 2;
+
+        // resident set size, unit KB
+        optional int64 rss = 3;
+
+        // proportional set size, unit KB
+        optional int64 pss = 4;
+
+        // unique set size, unit KB
+        optional int64 uss = 5;
+
+        // swap size, unit KB
+        optional int64 swap = 6;
+
+        // proportional swap size, unit KB
+        optional int64 pswap = 7;
+
+        // unique swap size, unit KB
+        optional int64 uswap = 8;
+
+        // zswap size, unit KB
+        optional int64 zswap = 9;
+
+        // process command
+        optional string cmdline = 10;
+    }
+    repeated Process processes = 1;
 
     // Summary
-    optional SummaryProto summary = 2;
-}
+    // Next Tag: 3
+    message Summary {
+        option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
-// Next Tag: 11
-message ProcessProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+        optional Process total = 1;
 
-    // ID of the process
-    optional int32 pid = 1;
+        // TODO: sync on how to use these values
+        message Zram {
+            option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
-    // virtual set size, unit KB
-    optional int64 vss = 2;
+            optional string raw_text = 1;
+        }
+        optional Zram zram = 2;
 
-    // resident set size, unit KB
-    optional int64 rss = 3;
+        message Ram {
+            option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
-    // proportional set size, unit KB
-    optional int64 pss = 4;
-
-    // unique set size, unit KB
-    optional int64 uss = 5;
-
-    // swap size, unit KB
-    optional int64 swap = 6;
-
-    // proportional swap size, unit KB
-    optional int64 pswap = 7;
-
-    // unique swap size, unit KB
-    optional int64 uswap = 8;
-
-    // zswap size, unit KB
-    optional int64 zswap = 9;
-
-    // process command
-    optional string cmdline = 10;
-}
-
-// Next Tag: 3
-message SummaryProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
-    optional ProcessProto total = 1;
-
-    optional ZramProto zram = 2;
-
-    optional RamProto ram = 3;
-}
-
-// TODO: sync on how to use these values
-message ZramProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
-    optional string raw_text = 1;
-}
-
-message RamProto {
-    option (android.msg_privacy).dest = DEST_AUTOMATIC;
-
-    optional string raw_text = 1;
+            optional string raw_text = 1;
+        }
+        optional Ram ram = 3;
+    }
+    optional Summary summary = 2;
 }
diff --git a/core/proto/android/os/ps.proto b/core/proto/android/os/ps.proto
index 9cce727..0ab92d7 100644
--- a/core/proto/android/os/ps.proto
+++ b/core/proto/android/os/ps.proto
@@ -22,7 +22,7 @@
 
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
-message PsDumpProto {
+message PsProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
     message Process {
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 02fc4da..6b92aa8 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -422,6 +422,8 @@
     optional SettingProto enable_deletion_helper_no_threshold_toggle = 340 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto notification_snooze_options = 341 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto enable_gnss_raw_meas_full_tracking = 346 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto install_carrier_app_notification_persistent = 356 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto install_carrier_app_notification_sleep_millis = 357 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto zram_enabled = 347 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto smart_replies_in_notifications_flags = 348 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto show_first_crash_dialog = 349 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6f3c25f..3a527b5 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -174,6 +174,10 @@
     <protected-broadcast
         android:name="android.bluetooth.headsetclient.profile.action.LAST_VTAG" />
     <protected-broadcast
+        android:name="android.bluetooth.hearingaid.profile.action.CONNECTION_STATE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.hearingaid.profile.action.PLAYING_STATE_CHANGED" />
+    <protected-broadcast
         android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.a2dp.profile.action.ACTIVE_DEVICE_CHANGED" />
diff --git a/core/res/res/drawable/ic_signal_cellular_alt_24px.xml b/core/res/res/drawable/ic_signal_cellular_alt_24px.xml
new file mode 100644
index 0000000..29f1f43
--- /dev/null
+++ b/core/res/res/drawable/ic_signal_cellular_alt_24px.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="20dp"
+    android:height="21dp"
+    android:viewportWidth="20"
+    android:viewportHeight="21">
+
+    <group
+            android:translateX="-31.000000"
+            android:translateY="-77.000000">
+        <group
+                android:translateX="24.000000"
+                android:translateY="72.000000">
+            <path
+                android:fillType="evenOdd"
+                android:strokeWidth="1"
+                android:pathData="M 0 0 H 32 V 32 H 0 V 0 Z" />
+            <path
+                android:fillColor="#4285F4"
+                android:strokeWidth="1"
+                android:pathData="M23,5 L27,5 L27,26 L23,26 L23,5 Z M7,18.125 L11,18.125 L11,26 L7,26 L7,18.125 Z
+M15,11.5625 L19,11.5625 L19,26 L15,26 L15,11.5625 Z" />
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/values-mcc302-mnc220/config.xml b/core/res/res/values-mcc302-mnc220/config.xml
index 9a3d736..8774334 100644
--- a/core/res/res/values-mcc302-mnc220/config.xml
+++ b/core/res/res/values-mcc302-mnc220/config.xml
@@ -36,7 +36,7 @@
 
     <!-- Values for GPS configuration (Telus) -->
     <string-array translatable="false" name="config_gpsParameters">
-        <item>SUPL_HOST=supl.telusmobility.com</item>
+        <item>SUPL_HOST=supl.google.com</item>
         <item>SUPL_PORT=7275</item>
         <item>SUPL_VER=0x20000</item>
         <item>SUPL_MODE=1</item>
diff --git a/core/res/res/values-mcc302-mnc221/config.xml b/core/res/res/values-mcc302-mnc221/config.xml
index 007fd04..05896b1 100644
--- a/core/res/res/values-mcc302-mnc221/config.xml
+++ b/core/res/res/values-mcc302-mnc221/config.xml
@@ -34,7 +34,7 @@
 
     <!-- Values for GPS configuration (Telus) -->
     <string-array translatable="false" name="config_gpsParameters">
-        <item>SUPL_HOST=supl.telusmobility.com</item>
+        <item>SUPL_HOST=supl.google.com</item>
         <item>SUPL_PORT=7275</item>
         <item>SUPL_VER=0x20000</item>
         <item>SUPL_MODE=1</item>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0218750..e994d37 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2328,6 +2328,10 @@
     <string name="config_customVpnAlwaysOnDisconnectedDialogComponent" translatable="false"
             >com.android.vpndialogs/com.android.vpndialogs.AlwaysOnDisconnectedDialog</string>
 
+    <!-- Name of the dialog that is used to install the carrier app when the SIM is inserted -->
+    <string name="config_carrierAppInstallDialogComponent" translatable="false"
+            >com.android.simappdialog/com.android.simappdialog.InstallCarrierAppActivity</string>
+
     <!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
     <string name="config_appsAuthorizedForSharedAccounts" translatable="false">;com.android.settings;</string>
 
@@ -3316,4 +3320,5 @@
     <string-array name="config_wearActivityModeRadios">
         <item>"wifi"</item>
     </string-array>
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0efb6f9..f691044 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -182,6 +182,8 @@
     <string name="notification_channel_voice_mail">Voicemail messages</string>
     <!-- Telephony notification channel name for a channel containing wifi calling status notifications. -->
     <string name="notification_channel_wfc">Wi-Fi calling</string>
+    <!-- Telephony notification channel name for a channel containing SIM notifications -->
+    <string name="notification_channel_sim">SIM status</string>
 
     <!-- Displayed to tell the user that peer changed TTY mode -->
     <string name="peerTtyModeFull">Peer requested TTY Mode FULL</string>
@@ -547,7 +549,7 @@
     <string name="global_action_voice_assist">Voice Assist</string>
 
     <!-- label for item that locks the phone and enforces that it can't be unlocked without strong authentication. [CHAR LIMIT=15] -->
-    <string name="global_action_lockdown">Enter lockdown</string>
+    <string name="global_action_lockdown">Lockdown</string>
 
     <!-- Text to use when the number in a notification info is too large
          (greater than status_bar_notification_info_maxnum, defined in
@@ -3194,10 +3196,12 @@
     <!-- See SIM_ADDED_DIALOG.  This is the button of that dialog. -->
     <string name="sim_restart_button">Restart</string>
     <!-- See Carrier_App_Dialog. This is the message of that dialog. -->
-    <string name="carrier_app_dialog_message">To get your new SIM working properly, you\'ll need to install and open an app from your carrier.</string>
-    <!-- See Carrier_App_Dialog. This is the button of that dialog. -->
-    <string name="carrier_app_dialog_button">GET THE APP</string>
-    <string name="carrier_app_dialog_not_now">NOT NOW</string>
+    <string name="install_carrier_app_notification_title">Activate mobile service</string>
+    <string name="install_carrier_app_notification_text">
+        Download the carrier app to activate your new SIM
+    </string>
+    <!-- See Carrier_App_Notification. This is the button of that dialog. -->
+    <string name="install_carrier_app_notification_button">Download app</string>
     <!-- See carrier_app_notification. This is the headline. -->
     <string name="carrier_app_notification_title">New SIM inserted</string>
     <string name="carrier_app_notification_text">Tap to set it up</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5a9dc7f..348f0b9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -574,6 +574,7 @@
   <java-symbol type="string" name="notification_channel_sms" />
   <java-symbol type="string" name="notification_channel_voice_mail" />
   <java-symbol type="string" name="notification_channel_wfc" />
+  <java-symbol type="string" name="notification_channel_sim" />
   <java-symbol type="string" name="SetupCallDefault" />
   <java-symbol type="string" name="accept" />
   <java-symbol type="string" name="activity_chooser_view_see_all" />
@@ -1383,6 +1384,7 @@
 
   <java-symbol type="drawable" name="ic_sim_card_multi_24px_clr" />
   <java-symbol type="drawable" name="ic_sim_card_multi_48px_clr" />
+  <java-symbol type="drawable" name="ic_signal_cellular_alt_24px" />
 
   <java-symbol type="drawable" name="stat_notify_mmcc_indication_icn" />
   <java-symbol type="drawable" name="autofilled_highlight"/>
@@ -2072,6 +2074,7 @@
   <java-symbol type="string" name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" />
   <java-symbol type="string" name="config_customVpnConfirmDialogComponent" />
   <java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" />
+  <java-symbol type="string" name="config_carrierAppInstallDialogComponent" />
   <java-symbol type="string" name="config_defaultNetworkScorerPackageName" />
   <java-symbol type="string" name="config_persistentDataPackageName" />
 
@@ -2771,9 +2774,9 @@
   <java-symbol type="array" name="resolver_target_actions_pin" />
   <java-symbol type="array" name="resolver_target_actions_unpin" />
 
-  <java-symbol type="string" name="carrier_app_dialog_message" />
-  <java-symbol type="string" name="carrier_app_dialog_button" />
-  <java-symbol type="string" name="carrier_app_dialog_not_now" />
+  <java-symbol type="string" name="install_carrier_app_notification_title" />
+  <java-symbol type="string" name="install_carrier_app_notification_text" />
+  <java-symbol type="string" name="install_carrier_app_notification_button" />
   <java-symbol type="string" name="carrier_app_notification_title" />
   <java-symbol type="string" name="carrier_app_notification_text" />
   <java-symbol type="string" name="negative_duration" />
@@ -3262,4 +3265,5 @@
 
   <java-symbol type="string" name="zen_upgrade_notification_title" />
   <java-symbol type="string" name="zen_upgrade_notification_content" />
+
 </resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index dc8ed9e..67c9754 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -402,6 +402,8 @@
                     Settings.Global.GPU_DEBUG_APP,
                     Settings.Global.GPU_DEBUG_LAYERS,
                     Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
+                    Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
+                    Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS,
                     Settings.Global.NETWORK_ACCESS_TIMEOUT_MS,
                     Settings.Global.WARNING_TEMPERATURE,
                     Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY,
@@ -468,6 +470,7 @@
                  Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
                  Settings.Secure.ASSIST_STRUCTURE_ENABLED,
                  Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
+                 Settings.Secure.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT,
                  Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
                  Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE,
                  Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH,
@@ -495,6 +498,7 @@
                  Settings.Secure.DOZE_ALWAYS_ON,
                  Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
                  Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION,
+                 Settings.Secure.ENABLED_INPUT_METHODS,  // Intentionally removed in P
                  Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT,
                  Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
                  Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
diff --git a/core/tests/featureflagtests/OWNERS b/core/tests/featureflagtests/OWNERS
new file mode 100644
index 0000000..1a8fd2b
--- /dev/null
+++ b/core/tests/featureflagtests/OWNERS
@@ -0,0 +1,2 @@
+sbasi@google.com
+zhfan@google.com
\ No newline at end of file
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 07df045..eacb727 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -22,7 +22,7 @@
 import android.annotation.Size;
 import android.graphics.Canvas.VertexMode;
 import android.text.GraphicsOperations;
-import android.text.PrecomputedText;
+import android.text.MeasuredText;
 import android.text.SpannableString;
 import android.text.SpannedString;
 import android.text.TextUtils;
@@ -487,8 +487,8 @@
             TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
             long measuredTextPtr = 0;
             int measuredTextOffset = 0;
-            if (text instanceof PrecomputedText) {
-                PrecomputedText mt = (PrecomputedText) text;
+            if (text instanceof MeasuredText) {
+                MeasuredText mt = (MeasuredText) text;
                 int paraIndex = mt.findParaIndex(start);
                 if (end <= mt.getParagraphEnd(paraIndex)) {
                     // Only suppor the same paragraph.
@@ -647,7 +647,7 @@
 
     private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
             int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
-            long nativePrecomputedText, int measuredTextOffset);
+            long nativeMeasuredText, int measuredTextOffset);
 
     private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
             long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint);
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 42dac38..ed147e9 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2835,16 +2835,6 @@
         return result;
     }
 
-    /**
-     * Returns true of the passed {@link Paint} will have the same effect on text measurement
-     *
-     * @param other A {@link Paint} object.
-     * @return true if the other {@link Paint} has the same effect on text measurement.
-     */
-    public boolean equalsForTextMeasurement(@NonNull Paint other) {
-        return nEqualsForTextMeasurement(mNativePaint, other.mNativePaint);
-    }
-
     // regular JNI
     private static native long nGetNativeFinalizer();
     private static native long nInit();
@@ -3012,6 +3002,4 @@
     private static native float nGetStrikeThruThickness(long paintPtr);
     @CriticalNative
     private static native void nSetTextSize(long paintPtr, float textSize);
-    @CriticalNative
-    private static native boolean nEqualsForTextMeasurement(long leftPaintPtr, long rightPaintPtr);
 }
diff --git a/media/OWNERS b/media/OWNERS
new file mode 100644
index 0000000..8f405e9
--- /dev/null
+++ b/media/OWNERS
@@ -0,0 +1,4 @@
+elaurent@google.com
+etalvala@google.com
+lajos@google.com
+marcone@google.com
diff --git a/native/android/OWNERS b/native/android/OWNERS
new file mode 100644
index 0000000..11d4be4
--- /dev/null
+++ b/native/android/OWNERS
@@ -0,0 +1,11 @@
+set noparent
+
+per-file libandroid_net.map.txt=ek@google.com
+per-file libandroid_net.map.txt=jchalard@google.com
+per-file libandroid_net.map.txt=lorenzo@google.com
+per-file libandroid_net.map.txt=satk@google.com
+
+per-file net.c=ek@google.com
+per-file net.c=jchalard@google.com
+per-file net.c=lorenzo@google.com
+per-file net.c=satk@google.com
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 44f68ec..d73a5d7 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -3117,6 +3117,8 @@
 
         private final Consumer<String> mCallback;
 
+        private boolean mIsTransformationStarted;
+
         public DocumentTransformer(Context context, PrintJobInfo printJob,
                 MutexFileProvider fileProvider, PrintAttributes attributes,
                 Consumer<String> callback) {
@@ -3144,29 +3146,35 @@
 
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
-            final IPdfEditor editor = IPdfEditor.Stub.asInterface(service);
-            new AsyncTask<Void, Void, String>() {
-                @Override
-                protected String doInBackground(Void... params) {
-                    // It's OK to access the data members as they are
-                    // final and this code is the last one to touch
-                    // them as shredding is the very last step, so the
-                    // UI is not interactive at this point.
-                    try {
-                        doTransform(editor);
-                        updatePrintJob();
-                        return null;
-                    } catch (IOException | RemoteException | IllegalStateException e) {
-                        return e.toString();
+            // We might get several onServiceConnected if the service crashes and restarts.
+            // mIsTransformationStarted makes sure that we only try once.
+            if (!mIsTransformationStarted) {
+                final IPdfEditor editor = IPdfEditor.Stub.asInterface(service);
+                new AsyncTask<Void, Void, String>() {
+                    @Override
+                    protected String doInBackground(Void... params) {
+                        // It's OK to access the data members as they are
+                        // final and this code is the last one to touch
+                        // them as shredding is the very last step, so the
+                        // UI is not interactive at this point.
+                        try {
+                            doTransform(editor);
+                            updatePrintJob();
+                            return null;
+                        } catch (IOException | RemoteException | IllegalStateException e) {
+                            return e.toString();
+                        }
                     }
-                }
 
-                @Override
-                protected void onPostExecute(String error) {
-                    mContext.unbindService(DocumentTransformer.this);
-                    mCallback.accept(error);
-                }
-            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+                    @Override
+                    protected void onPostExecute(String error) {
+                        mContext.unbindService(DocumentTransformer.this);
+                        mCallback.accept(error);
+                    }
+                }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+
+                mIsTransformationStarted = true;
+            }
         }
 
         @Override
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
index 7c2e55f..cf73aac 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -285,6 +285,11 @@
             final int position = ((AdapterContextMenuInfo) menuInfo).position;
             PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
 
+            // Printer is null if this is a context menu for the "add printer" entry
+            if (printer == null) {
+                return;
+            }
+
             menu.setHeaderTitle(printer.getName());
 
             // Add the select menu item if applicable.
diff --git a/packages/SettingsLib/res/drawable/ic_bt_hearing_aid.xml b/packages/SettingsLib/res/drawable/ic_bt_hearing_aid.xml
new file mode 100644
index 0000000..e14c99b
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_bt_hearing_aid.xml
@@ -0,0 +1,24 @@
+<!--
+     Copyright (C) 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,20c-0.29,0 -0.56,-0.06 -0.76,-0.15 -0.71,-0.37 -1.21,-0.88 -1.71,-2.38 -0.51,-1.56 -1.47,-2.29 -2.39,-3 -0.79,-0.61 -1.61,-1.24 -2.32,-2.53C9.29,10.98 9,9.93 9,9c0,-2.8 2.2,-5 5,-5s5,2.2 5,5h2c0,-3.93 -3.07,-7 -7,-7S7,5.07 7,9c0,1.26 0.38,2.65 1.07,3.9 0.91,1.65 1.98,2.48 2.85,3.15 0.81,0.62 1.39,1.07 1.71,2.05 0.6,1.82 1.37,2.84 2.73,3.55 0.51,0.23 1.07,0.35 1.64,0.35 2.21,0 4,-1.79 4,-4h-2c0,1.1 -0.9,2 -2,2zM7.64,2.64L6.22,1.22C4.23,3.21 3,5.96 3,9s1.23,5.79 3.22,7.78l1.41,-1.41C6.01,13.74 5,11.49 5,9s1.01,-4.74 2.64,-6.36zM11.5,9c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5 -1.12,-2.5 -2.5,-2.5 -2.5,1.12 -2.5,2.5z"/>
+</vector>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index e6f4ec6..c78f454 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -177,6 +177,11 @@
     <!-- Bluetooth settings. Similar to bluetooth_profile_a2dp_high_quality, but used when the device supports high quality audio but we don't know which codec that will be used. -->
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec">HD audio</string>
 
+    <!-- Bluetooth settings.  The user-visible string that is used whenever referring to the Hearing Aid profile. -->
+    <string name="bluetooth_profile_hearing_aid">Hearing Aid</string>
+    <!-- Bluetooth settings.  Connection options screen.  The summary for the Hearing Aid checkbox preference when Hearing Aid is connected. -->
+    <string name="bluetooth_hearing_aid_profile_summary_connected">Connected to Hearing Aid</string>
+
     <!-- Bluetooth settings.  Connection options screen.  The summary for the A2DP checkbox preference when A2DP is connected. -->
     <string name="bluetooth_a2dp_profile_summary_connected">Connected to media audio</string>
     <!-- Bluetooth settings.  Connection options screen.  The summary for the headset checkbox preference when headset is connected. -->
@@ -214,6 +219,8 @@
          for the HID checkbox preference that describes how checking it
          will set the HID profile as preferred. -->
     <string name="bluetooth_hid_profile_summary_use_for">Use for input</string>
+    <!-- Bluetooth settings.  Connection options screen.  The summary for the Hearing Aid checkbox preference that describes how checking it will set the Hearing Aid profile as preferred. -->
+    <string name="bluetooth_hearing_aid_profile_summary_use_for">Use for Hearing Aid</string>
 
     <!-- Button text for accepting an incoming pairing request. [CHAR LIMIT=20] -->
     <string name="bluetooth_pairing_accept">Pair</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 1f67dfb..9947dec 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -1,6 +1,7 @@
 package com.android.settingslib;
 
 import android.annotation.ColorInt;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageInfo;
@@ -17,10 +18,13 @@
 import android.location.LocationManager;
 import android.net.ConnectivityManager;
 import android.os.BatteryManager;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.print.PrintManager;
 import android.provider.Settings;
+
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.settingslib.wrapper.LocationManagerWrapper;
@@ -30,6 +34,9 @@
 
     private static final String CURRENT_MODE_KEY = "CURRENT_MODE";
     private static final String NEW_MODE_KEY = "NEW_MODE";
+    @VisibleForTesting
+    static final String STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY =
+            "ro.storage_manager.show_opt_in";
 
     private static Signature[] sSystemSignature;
     private static String sPermissionControllerPackageName;
@@ -37,11 +44,11 @@
     private static String sSharedSystemSharedLibPackageName;
 
     static final int[] WIFI_PIE = {
-        com.android.internal.R.drawable.ic_wifi_signal_0,
-        com.android.internal.R.drawable.ic_wifi_signal_1,
-        com.android.internal.R.drawable.ic_wifi_signal_2,
-        com.android.internal.R.drawable.ic_wifi_signal_3,
-        com.android.internal.R.drawable.ic_wifi_signal_4
+            com.android.internal.R.drawable.ic_wifi_signal_0,
+            com.android.internal.R.drawable.ic_wifi_signal_1,
+            com.android.internal.R.drawable.ic_wifi_signal_2,
+            com.android.internal.R.drawable.ic_wifi_signal_3,
+            com.android.internal.R.drawable.ic_wifi_signal_4
     };
 
     public static void updateLocationEnabled(Context context, boolean enabled, int userId,
@@ -262,7 +269,7 @@
      */
     public static boolean isSystemPackage(Resources resources, PackageManager pm, PackageInfo pkg) {
         if (sSystemSignature == null) {
-            sSystemSignature = new Signature[]{ getSystemSignature(pm) };
+            sSystemSignature = new Signature[]{getSystemSignature(pm)};
         }
         if (sPermissionControllerPackageName == null) {
             sPermissionControllerPackageName = pm.getPermissionControllerPackageName();
@@ -274,7 +281,7 @@
             sSharedSystemSharedLibPackageName = pm.getSharedSystemSharedLibraryPackageName();
         }
         return (sSystemSignature[0] != null
-                        && sSystemSignature[0].equals(getFirstSignature(pkg)))
+                && sSystemSignature[0].equals(getFirstSignature(pkg)))
                 || pkg.packageName.equals(sPermissionControllerPackageName)
                 || pkg.packageName.equals(sServicesSystemSharedLibPackageName)
                 || pkg.packageName.equals(sSharedSystemSharedLibPackageName)
@@ -312,7 +319,6 @@
      * Returns the Wifi icon resource for a given RSSI level.
      *
      * @param level The number of bars to show (0-4)
-     *
      * @throws IllegalArgumentException if an invalid RSSI level is given.
      */
     public static int getWifiIconResource(int level) {
@@ -342,4 +348,19 @@
         return !context.getSystemService(ConnectivityManager.class)
                 .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
     }
+
+    /** Returns if the automatic storage management feature is turned on or not. **/
+    public static boolean isStorageManagerEnabled(Context context) {
+        boolean isDefaultOn;
+        try {
+            // Turn off by default if the opt-in was shown.
+            isDefaultOn = !SystemProperties.getBoolean(STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY, true);
+        } catch (Resources.NotFoundException e) {
+            isDefaultOn = false;
+        }
+        return Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
+                isDefaultOn ? 1 : 0)
+                != 0;
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
new file mode 100644
index 0000000..8f9e4635
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 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 com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothCodecStatus;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class HearingAidProfile implements LocalBluetoothProfile {
+    private static final String TAG = "HearingAidProfile";
+    private static boolean V = true;
+
+    private Context mContext;
+
+    private BluetoothHearingAid mService;
+    private boolean mIsProfileReady;
+
+    private final LocalBluetoothAdapter mLocalAdapter;
+    private final CachedBluetoothDeviceManager mDeviceManager;
+
+    static final String NAME = "HearingAid";
+    private final LocalBluetoothProfileManager mProfileManager;
+
+    // Order of this profile in device profiles list
+    private static final int ORDINAL = 1;
+
+    // These callbacks run on the main thread.
+    private final class HearingAidServiceListener
+            implements BluetoothProfile.ServiceListener {
+
+        public void onServiceConnected(int profile, BluetoothProfile proxy) {
+            if (V) Log.d(TAG,"Bluetooth service connected");
+            mService = (BluetoothHearingAid) proxy;
+            // We just bound to the service, so refresh the UI for any connected HearingAid devices.
+            List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+            while (!deviceList.isEmpty()) {
+                BluetoothDevice nextDevice = deviceList.remove(0);
+                CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+                // we may add a new device here, but generally this should not happen
+                if (device == null) {
+                    Log.w(TAG, "HearingAidProfile found new device: " + nextDevice);
+                    device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+                }
+                device.onProfileStateChanged(HearingAidProfile.this, BluetoothProfile.STATE_CONNECTED);
+                device.refresh();
+            }
+            mIsProfileReady=true;
+        }
+
+        public void onServiceDisconnected(int profile) {
+            if (V) Log.d(TAG,"Bluetooth service disconnected");
+            mIsProfileReady=false;
+        }
+    }
+
+    public boolean isProfileReady() {
+        return mIsProfileReady;
+    }
+
+    HearingAidProfile(Context context, LocalBluetoothAdapter adapter,
+            CachedBluetoothDeviceManager deviceManager,
+            LocalBluetoothProfileManager profileManager) {
+        mContext = context;
+        mLocalAdapter = adapter;
+        mDeviceManager = deviceManager;
+        mProfileManager = profileManager;
+        mLocalAdapter.getProfileProxy(context, new HearingAidServiceListener(),
+                BluetoothProfile.HEARING_AID);
+    }
+
+    public boolean isConnectable() {
+        return true;
+    }
+
+    public boolean isAutoConnectable() {
+        return true;
+    }
+
+    public List<BluetoothDevice> getConnectedDevices() {
+        if (mService == null) return new ArrayList<BluetoothDevice>(0);
+        return mService.getDevicesMatchingConnectionStates(
+              new int[] {BluetoothProfile.STATE_CONNECTED,
+                         BluetoothProfile.STATE_CONNECTING,
+                         BluetoothProfile.STATE_DISCONNECTING});
+    }
+
+    public boolean connect(BluetoothDevice device) {
+        if (mService == null) return false;
+        return mService.connect(device);
+    }
+
+    public boolean disconnect(BluetoothDevice device) {
+        if (mService == null) return false;
+        // Downgrade priority as user is disconnecting the hearing aid.
+        if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON){
+            mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+        }
+        return mService.disconnect(device);
+    }
+
+    public int getConnectionStatus(BluetoothDevice device) {
+        if (mService == null) {
+            return BluetoothProfile.STATE_DISCONNECTED;
+        }
+        return mService.getConnectionState(device);
+    }
+
+    public boolean isPreferred(BluetoothDevice device) {
+        if (mService == null) return false;
+        return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+    }
+
+    public int getPreferred(BluetoothDevice device) {
+        if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+        return mService.getPriority(device);
+    }
+
+    public void setPreferred(BluetoothDevice device, boolean preferred) {
+        if (mService == null) return;
+        if (preferred) {
+            if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+                mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+            }
+        } else {
+            mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+        }
+    }
+
+    public int getVolume() {
+        if (mService == null) {
+            return 0;
+        }
+        return mService.getVolume();
+    }
+
+    public void setVolume(int volume) {
+        if (mService == null) {
+            return;
+        }
+        mService.setVolume(volume);
+    }
+
+    public long getHiSyncId(BluetoothDevice device) {
+        if (mService == null) {
+            return BluetoothHearingAid.HI_SYNC_ID_INVALID;
+        }
+        return mService.getHiSyncId(device);
+    }
+
+    public int getDeviceSide(BluetoothDevice device) {
+        if (mService == null) {
+            return BluetoothHearingAid.SIDE_LEFT;
+        }
+        return mService.getDeviceSide(device);
+    }
+
+    public int getDeviceMode(BluetoothDevice device) {
+        if (mService == null) {
+            return BluetoothHearingAid.MODE_MONAURAL;
+        }
+        return mService.getDeviceMode(device);
+    }
+
+    public String toString() {
+        return NAME;
+    }
+
+    public int getOrdinal() {
+        return ORDINAL;
+    }
+
+    public int getNameResource(BluetoothDevice device) {
+        return R.string.bluetooth_profile_hearing_aid;
+    }
+
+    public int getSummaryResourceForDevice(BluetoothDevice device) {
+        int state = getConnectionStatus(device);
+        switch (state) {
+            case BluetoothProfile.STATE_DISCONNECTED:
+                return R.string.bluetooth_hearing_aid_profile_summary_use_for;
+
+            case BluetoothProfile.STATE_CONNECTED:
+                return R.string.bluetooth_hearing_aid_profile_summary_connected;
+
+            default:
+                return Utils.getConnectionStateSummary(state);
+        }
+    }
+
+    public int getDrawableResource(BluetoothClass btClass) {
+        return R.drawable.ic_bt_hearing_aid;
+    }
+
+    protected void finalize() {
+        if (V) Log.d(TAG, "finalize()");
+        if (mService != null) {
+            try {
+                BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.HEARING_AID,
+                                                                       mService);
+                mService = null;
+            }catch (Throwable t) {
+                Log.w(TAG, "Error cleaning up Hearing Aid proxy", t);
+            }
+        }
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 991d922..34a099c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -21,6 +21,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothHeadsetClient;
+import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothHidHost;
 import android.bluetooth.BluetoothMap;
 import android.bluetooth.BluetoothMapClient;
@@ -91,6 +92,7 @@
     private final PbapServerProfile mPbapProfile;
     private final boolean mUsePbapPce;
     private final boolean mUseMapClient;
+    private HearingAidProfile mHearingAidProfile;
 
     /**
      * Mapping from profile name, e.g. "HEADSET" to profile object.
@@ -143,10 +145,14 @@
 
         //Create PBAP server profile
         if(DEBUG) Log.d(TAG, "Adding local PBAP profile");
+
         mPbapProfile = new PbapServerProfile(context);
         addProfile(mPbapProfile, PbapServerProfile.NAME,
              BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED);
 
+        mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager, this);
+        addProfile(mHearingAidProfile, HearingAidProfile.NAME,
+                   BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
         if (DEBUG) Log.d(TAG, "LocalBluetoothProfileManager construction complete");
     }
 
@@ -254,6 +260,18 @@
                 "Warning: PBAP Client profile was previously added but the UUID is now missing.");
         }
 
+        //Hearing Aid Client
+        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)) {
+            if (mHearingAidProfile == null) {
+                if(DEBUG) Log.d(TAG, "Adding local Hearing Aid profile");
+                mHearingAidProfile = new HearingAidProfile(mContext, mLocalAdapter, mDeviceManager, this);
+                addProfile(mHearingAidProfile, HearingAidProfile.NAME,
+                        BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
+            }
+        } else if (mHearingAidProfile != null) {
+            Log.w(TAG, "Warning: Hearing Aid profile was previously added but the UUID is now missing.");
+        }
+
         mEventManager.registerProfileIntentReceiver();
 
         // There is no local SDP record for HID and Settings app doesn't control PBAP Server.
@@ -416,6 +434,10 @@
         return mMapClientProfile;
     }
 
+    public HearingAidProfile getHearingAidProfile() {
+        return mHearingAidProfile;
+    }
+
     /**
      * Fill in a list of LocalBluetoothProfile objects that are supported by
      * the local device and the remote device.
@@ -515,6 +537,12 @@
             removedProfiles.remove(mPbapClientProfile);
         }
 
+        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid) &&
+            mHearingAidProfile != null) {
+            profiles.add(mHearingAidProfile);
+            removedProfiles.remove(mHearingAidProfile);
+        }
+
         if (DEBUG) {
             Log.d(TAG,"New Profiles" + profiles.toString());
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
index 1a54d6a..b98f27e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java
@@ -76,6 +76,8 @@
     protected Uri mForeverId;
     private int mBucketIndex = -1;
 
+    @VisibleForTesting
+    protected NotificationManager mNotificationManager;
     private AlarmManager mAlarmManager;
     private int mUserId;
     private boolean mAttached;
@@ -98,7 +100,7 @@
     }
 
     public Dialog createDialog() {
-        NotificationManager noMan = (NotificationManager) mContext.
+        mNotificationManager = (NotificationManager) mContext.
                 getSystemService(Context.NOTIFICATION_SERVICE);
         mForeverId =  Condition.newId(mContext).appendPath("forever").build();
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
@@ -131,7 +133,8 @@
                                     Slog.d(TAG, "Invalid manual condition: " + tag.condition);
                                 }
                                 // always triggers priority-only dnd with chosen condition
-                                noMan.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                                mNotificationManager.setZenMode(
+                                        Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
                                         getRealConditionId(tag.condition), TAG);
                             }
                         });
@@ -465,7 +468,16 @@
         mZenAlarmWarning.setVisibility(warningText == null ? View.GONE : View.VISIBLE);
     }
 
-    private String computeAlarmWarningText(Condition condition) {
+    @VisibleForTesting
+    protected String computeAlarmWarningText(Condition condition) {
+        boolean allowAlarms = (mNotificationManager.getNotificationPolicy().priorityCategories
+                & NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS) != 0;
+
+        // don't show alarm warning if alarms are allowed to bypass dnd
+        if (allowAlarms) {
+            return null;
+        }
+
         final long now = System.currentTimeMillis();
         final long nextAlarm = getNextAlarm();
         if (nextAlarm < now) {
@@ -483,14 +495,19 @@
         if (warningRes == 0) {
             return null;
         }
+
+        return mContext.getResources().getString(warningRes, getTime(nextAlarm, now));
+    }
+
+    @VisibleForTesting
+    protected String getTime(long nextAlarm, long now) {
         final boolean soon = (nextAlarm - now) < 24 * 60 * 60 * 1000;
         final boolean is24 = DateFormat.is24HourFormat(mContext, ActivityManager.getCurrentUser());
         final String skeleton = soon ? (is24 ? "Hm" : "hma") : (is24 ? "EEEHm" : "EEEhma");
         final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
         final CharSequence formattedTime = DateFormat.format(pattern, nextAlarm);
         final int templateRes = soon ? R.string.alarm_template : R.string.alarm_template_far;
-        final String template = mContext.getResources().getString(templateRes, formattedTime);
-        return mContext.getResources().getString(warningRes, template);
+        return mContext.getResources().getString(templateRes, formattedTime);
     }
 
     // used as the view tag on condition rows
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index f699440..b380ac5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -17,6 +17,7 @@
 package com.android.settingslib.wifi;
 
 import android.annotation.IntDef;
+import android.annotation.MainThread;
 import android.annotation.Nullable;
 import android.app.AppGlobals;
 import android.content.Context;
@@ -42,6 +43,8 @@
 import android.net.wifi.WifiNetworkScoreCache;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -57,6 +60,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.R;
+import com.android.settingslib.utils.ThreadUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -68,7 +72,14 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 
-
+/**
+ * Represents a selectable Wifi Network for use in various wifi selection menus backed by
+ * {@link WifiTracker}.
+ *
+ * <p>An AccessPoint, which would be more fittingly named "WifiNetwork", is an aggregation of
+ * {@link ScanResult ScanResults} along with pertinent metadata (e.g. current connection info,
+ * network scores) required to successfully render the network to the user.
+ */
 public class AccessPoint implements Comparable<AccessPoint> {
     static final String TAG = "SettingsLib.AccessPoint";
 
@@ -288,11 +299,6 @@
         mId = sLastId.incrementAndGet();
     }
 
-    AccessPoint(Context context, AccessPoint other) {
-        mContext = context;
-        copyFrom(other);
-    }
-
     AccessPoint(Context context, Collection<ScanResult> results) {
         mContext = context;
 
@@ -346,33 +352,6 @@
     }
 
     /**
-     * Copy accesspoint information. NOTE: We do not copy tag information because that is never
-     * set on the internal copy.
-     */
-    void copyFrom(AccessPoint that) {
-        this.ssid = that.ssid;
-        this.bssid = that.bssid;
-        this.security = that.security;
-        this.mKey = that.mKey;
-        this.networkId = that.networkId;
-        this.pskType = that.pskType;
-        this.mConfig = that.mConfig; //TODO: Watch out, this object is mutated.
-        this.mRssi = that.mRssi;
-        this.mInfo = that.mInfo;
-        this.mNetworkInfo = that.mNetworkInfo;
-        this.mScanResults.clear();
-        this.mScanResults.addAll(that.mScanResults);
-        this.mScoredNetworkCache.clear();
-        this.mScoredNetworkCache.putAll(that.mScoredNetworkCache);
-        this.mId = that.mId;
-        this.mSpeed = that.mSpeed;
-        this.mIsScoredNetworkMetered = that.mIsScoredNetworkMetered;
-        this.mIsCarrierAp = that.mIsCarrierAp;
-        this.mCarrierApEapType = that.mCarrierApEapType;
-        this.mCarrierName = that.mCarrierName;
-    }
-
-    /**
     * Returns a negative integer, zero, or a positive integer if this AccessPoint is less than,
     * equal to, or greater than the other AccessPoint.
     *
@@ -467,7 +446,7 @@
         }
         builder.append(",metered=").append(isMetered());
 
-        if (WifiTracker.sVerboseLogging) {
+        if (isVerboseLoggingEnabled()) {
             builder.append(",rssi=").append(mRssi);
             builder.append(",scan cache size=").append(mScanResults.size());
         }
@@ -546,7 +525,7 @@
         mSpeed = generateAverageSpeedForSsid();
 
         boolean changed = oldSpeed != mSpeed;
-        if(WifiTracker.sVerboseLogging && changed) {
+        if(isVerboseLoggingEnabled() && changed) {
             Log.i(TAG, String.format("%s: Set speed to %d", ssid, mSpeed));
         }
         return changed;
@@ -577,7 +556,7 @@
             }
         }
         int speed = count == 0 ? Speed.NONE : totalSpeed / count;
-        if (WifiTracker.sVerboseLogging) {
+        if (isVerboseLoggingEnabled()) {
             Log.i(TAG, String.format("%s generated fallback speed is: %d", getSsidStr(), speed));
         }
         return roundToClosestSpeedEnum(speed);
@@ -913,7 +892,7 @@
             }
         }
 
-        if (WifiTracker.sVerboseLogging) {
+        if (isVerboseLoggingEnabled()) {
             summary.append(WifiUtils.buildLoggingSummary(this, config));
         }
 
@@ -1070,12 +1049,12 @@
             // Only update labels on visible rssi changes
             updateSpeed();
             if (mAccessPointListener != null) {
-                mAccessPointListener.onLevelChanged(this);
+                ThreadUtils.postOnMainThread(() -> mAccessPointListener.onLevelChanged(this));
             }
         }
 
         if (mAccessPointListener != null) {
-            mAccessPointListener.onAccessPointChanged(this);
+            ThreadUtils.postOnMainThread(() -> mAccessPointListener.onAccessPointChanged(this));
         }
 
         if (!scanResults.isEmpty()) {
@@ -1123,10 +1102,10 @@
             mNetworkInfo = null;
         }
         if (updated && mAccessPointListener != null) {
-            mAccessPointListener.onAccessPointChanged(this);
+            ThreadUtils.postOnMainThread(() -> mAccessPointListener.onAccessPointChanged(this));
 
             if (oldLevel != getLevel() /* current level */) {
-                mAccessPointListener.onLevelChanged(this);
+                ThreadUtils.postOnMainThread(() -> mAccessPointListener.onLevelChanged(this));
             }
         }
 
@@ -1137,7 +1116,7 @@
         mConfig = config;
         networkId = config != null ? config.networkId : WifiConfiguration.INVALID_NETWORK_ID;
         if (mAccessPointListener != null) {
-            mAccessPointListener.onAccessPointChanged(this);
+            ThreadUtils.postOnMainThread(() -> mAccessPointListener.onAccessPointChanged(this));
         }
     }
 
@@ -1333,8 +1312,44 @@
         return string;
     }
 
+    /**
+     * Callbacks relaying changes to the AccessPoint representation.
+     *
+     * <p>All methods are invoked on the Main Thread.
+     */
     public interface AccessPointListener {
-        void onAccessPointChanged(AccessPoint accessPoint);
-        void onLevelChanged(AccessPoint accessPoint);
+        /**
+         * Indicates a change to the externally visible state of the AccessPoint trigger by an
+         * update of ScanResults, saved configuration state, connection state, or score
+         * (labels/metered) state.
+         *
+         * <p>Clients should refresh their view of the AccessPoint to match the updated state when
+         * this is invoked. Overall this method is extraneous if clients are listening to
+         * {@link WifiTracker.WifiListener#onAccessPointsChanged()} callbacks.
+         *
+         * <p>Examples of changes include signal strength, connection state, speed label, and
+         * generally anything that would impact the summary string.
+         *
+         * @param accessPoint The accessPoint object the listener was registered on which has
+         *                    changed
+         */
+        @MainThread void onAccessPointChanged(AccessPoint accessPoint);
+
+        /**
+         * Indicates the "wifi pie signal level" has changed, retrieved via calls to
+         * {@link AccessPoint#getLevel()}.
+         *
+         * <p>This call is a subset of {@link #onAccessPointChanged(AccessPoint)} , hence is also
+         * extraneous if the client is already reacting to that or the
+         * {@link WifiTracker.WifiListener#onAccessPointsChanged()} callbacks.
+         *
+         * @param accessPoint The accessPoint object the listener was registered on whose level has
+         *                    changed
+         */
+        @MainThread void onLevelChanged(AccessPoint accessPoint);
+    }
+
+    private static boolean isVerboseLoggingEnabled() {
+        return WifiTracker.sVerboseLogging || Log.isLoggable(TAG, Log.VERBOSE);
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index fac585e..ae544dd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -15,6 +15,7 @@
  */
 package com.android.settingslib.wifi;
 
+import android.annotation.AnyThread;
 import android.annotation.MainThread;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -48,8 +49,6 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
 import android.widget.Toast;
 
 import com.android.settingslib.R;
@@ -58,6 +57,7 @@
 import com.android.settingslib.core.lifecycle.events.OnDestroy;
 import com.android.settingslib.core.lifecycle.events.OnStart;
 import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.utils.ThreadUtils;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -88,8 +88,17 @@
         return Log.isLoggable(TAG, Log.DEBUG);
     }
 
-    /** verbose logging flag. this flag is set thru developer debugging options
-     * and used so as to assist with in-the-field WiFi connectivity debugging  */
+    private static boolean isVerboseLoggingEnabled() {
+        return WifiTracker.sVerboseLogging || Log.isLoggable(TAG, Log.VERBOSE);
+    }
+
+    /**
+     * Verbose logging flag set thru developer debugging options and used so as to assist with
+     * in-the-field WiFi connectivity debugging.
+     *
+     * <p>{@link #isVerboseLoggingEnabled()} should be read rather than referencing this value
+     * directly, to ensure adb TAG level verbose settings are respected.
+     */
     public static boolean sVerboseLogging;
 
     // TODO: Allow control of this?
@@ -104,7 +113,6 @@
     private final NetworkRequest mNetworkRequest;
     private final AtomicBoolean mConnected = new AtomicBoolean(false);
     private final WifiListener mListener;
-    @VisibleForTesting MainHandler mMainHandler;
     @VisibleForTesting WorkHandler mWorkHandler;
     private HandlerThread mWorkThread;
 
@@ -113,35 +121,17 @@
     @GuardedBy("mLock")
     private boolean mRegistered;
 
-    /**
-     * The externally visible access point list.
-     *
-     * Updated using main handler. Clone of this collection is returned from
-     * {@link #getAccessPoints()}
-     */
-    private final List<AccessPoint> mAccessPoints = new ArrayList<>();
-
-    /**
-     * The internal list of access points, synchronized on itself.
-     *
-     * Never exposed outside this class.
-     */
+    /** The list of AccessPoints, aggregated visible ScanResults with metadata. */
     @GuardedBy("mLock")
     private final List<AccessPoint> mInternalAccessPoints = new ArrayList<>();
 
     /**
     * Synchronization lock for managing concurrency between main and worker threads.
     *
-    * <p>This lock should be held for all background work.
-    * TODO(b/37674366): Remove the worker thread so synchronization is no longer necessary.
+    * <p>This lock should be held for all modifications to {@link #mInternalAccessPoints}.
     */
     private final Object mLock = new Object();
 
-    //visible to both worker and main thread.
-    @GuardedBy("mLock")
-    private final AccessPointListenerAdapter mAccessPointListenerAdapter
-            = new AccessPointListenerAdapter();
-
     private final HashMap<String, Integer> mSeenBssids = new HashMap<>();
 
     // TODO(sghuman): Change this to be keyed on AccessPoint.getKey
@@ -161,6 +151,12 @@
     @VisibleForTesting
     Scanner mScanner;
 
+    /**
+     * Tracks whether fresh scan results have been received since scanning start.
+     *
+     * <p>If this variable is false, we will not evict the scan result cache or invoke callbacks
+     * so that we do not update the UI with stale data / clear out existing UI elements prematurely.
+     */
     @GuardedBy("mLock")
     private boolean mStaleScanResults = true;
 
@@ -209,12 +205,11 @@
             NetworkScoreManager networkScoreManager,
             IntentFilter filter) {
         mContext = context;
-        mMainHandler = new MainHandler(Looper.getMainLooper());
         mWifiManager = wifiManager;
         mListener = new WifiListenerWrapper(wifiListener);
         mConnectivityManager = connectivityManager;
 
-        // check if verbose logging has been turned on or off
+        // check if verbose logging developer option has been turned on or off
         sVerboseLogging = (mWifiManager.getVerboseLoggingLevel() > 0);
 
         mFilter = filter;
@@ -226,6 +221,7 @@
 
         mNetworkScoreManager = networkScoreManager;
 
+        // TODO(sghuman): Remove this and create less hacky solution for testing
         final HandlerThread workThread = new HandlerThread(TAG
                 + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
                 Process.THREAD_PRIORITY_BACKGROUND);
@@ -238,6 +234,8 @@
      * @param workThread substitute Handler thread, for testing purposes only
      */
     @VisibleForTesting
+    // TODO(sghuman): Remove this method, this needs to happen in a factory method and be passed in
+    // during construction
     void setWorkThread(HandlerThread workThread) {
         mWorkThread = workThread;
         mWorkHandler = new WorkHandler(workThread.getLooper());
@@ -270,32 +268,29 @@
             mLastNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
 
             final List<ScanResult> newScanResults = mWifiManager.getScanResults();
-            if (sVerboseLogging) {
+            if (isVerboseLoggingEnabled()) {
                 Log.i(TAG, "Fetched scan results: " + newScanResults);
             }
 
             List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
             mInternalAccessPoints.clear();
             updateAccessPointsLocked(newScanResults, configs);
-
-            // Synchronously copy access points
-            mMainHandler.removeMessages(MainHandler.MSG_ACCESS_POINT_CHANGED);
-            mMainHandler.handleMessage(
-                    Message.obtain(mMainHandler, MainHandler.MSG_ACCESS_POINT_CHANGED));
-            if (sVerboseLogging) {
-                Log.i(TAG, "force update - external access point list:\n" + mAccessPoints);
-            }
         }
     }
 
     /**
      * Temporarily stop scanning for wifi networks.
+     *
+     * <p>Sets {@link #mStaleScanResults} to true.
      */
-    public void pauseScanning() {
+    private void pauseScanning() {
         if (mScanner != null) {
             mScanner.pause();
             mScanner = null;
         }
+        synchronized (mLock) {
+            mStaleScanResults = true;
+        }
     }
 
     /**
@@ -387,11 +382,9 @@
                 mRegistered = false;
             }
             unregisterScoreCache();
-            pauseScanning();
+            pauseScanning(); // and set mStaleScanResults
 
             mWorkHandler.removePendingMessages();
-            mMainHandler.removePendingMessages();
-            mStaleScanResults = true;
         }
     }
 
@@ -409,12 +402,19 @@
     }
 
     /**
-     * Gets the current list of access points. Should be called from main thread, otherwise
-     * expect inconsistencies
+     * Gets the current list of access points.
+     *
+     * <p>This method is can be called on an abitrary thread by clients, but is normally called on
+     * the UI Thread by the rendering App.
      */
-    @MainThread
+    @AnyThread
     public List<AccessPoint> getAccessPoints() {
-        return new ArrayList<>(mAccessPoints);
+        // TODO(sghuman): Investigate how to eliminate or reduce the need for locking now that we
+        // have transitioned to a single worker thread model.
+
+        synchronized (mLock) {
+            return new ArrayList<>(mInternalAccessPoints);
+        }
     }
 
     public WifiManager getManager() {
@@ -447,6 +447,8 @@
     }
 
     private void handleResume() {
+        // TODO(sghuman): Investigate removing this and replacing it with a cache eviction call
+        // instead.
         mScanResultCache.clear();
         mSeenBssids.clear();
     }
@@ -509,7 +511,7 @@
     private void updateAccessPoints() {
         List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
         final List<ScanResult> newScanResults = mWifiManager.getScanResults();
-        if (sVerboseLogging) {
+        if (isVerboseLoggingEnabled()) {
             Log.i(TAG, "Fetched scan results: " + newScanResults);
         }
 
@@ -524,11 +526,15 @@
      * Update the internal list of access points.
      *
      * <p>Do not call directly (except for forceUpdate), use {@link #updateAccessPoints()} which
-     * respects {@link #mStaleScanResults}.
+     * acquires the lock first.
      */
     @GuardedBy("mLock")
     private void updateAccessPointsLocked(final List<ScanResult> newScanResults,
             List<WifiConfiguration> configs) {
+        // TODO(sghuman): Reduce the synchronization time by only holding the lock when
+        // modifying lists exposed to operations on the MainThread (getAccessPoints, stopTracking,
+        // startTracking, etc).
+
         WifiConfiguration connectionConfig = null;
         if (mLastInfo != null) {
             connectionConfig = getWifiConfigurationForNetworkId(
@@ -634,7 +640,7 @@
         mInternalAccessPoints.clear();
         mInternalAccessPoints.addAll(accessPoints);
 
-        mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED);
+        conditionallyNotifyListeners();
     }
 
     @VisibleForTesting
@@ -650,7 +656,6 @@
             }
         }
         final AccessPoint accessPoint = new AccessPoint(mContext, scanResults);
-        accessPoint.setListener(mAccessPointListenerAdapter);
         return accessPoint;
     }
 
@@ -661,16 +666,17 @@
             if (cache.get(i).matches(config)) {
                 AccessPoint ret = cache.remove(i);
                 ret.loadConfig(config);
+
                 return ret;
             }
         }
         final AccessPoint accessPoint = new AccessPoint(mContext, config);
-        accessPoint.setListener(mAccessPointListenerAdapter);
         return accessPoint;
     }
 
     private void updateNetworkInfo(NetworkInfo networkInfo) {
-        /* sticky broadcasts can call this when wifi is disabled */
+
+        /* Sticky broadcasts can call this when wifi is disabled */
         if (!mWifiManager.isWifiEnabled()) {
             clearAccessPointsAndConditionallyUpdate();
             return;
@@ -681,6 +687,10 @@
             if (DBG()) {
                 Log.d(TAG, "mLastNetworkInfo set: " + mLastNetworkInfo);
             }
+
+            if(networkInfo.isConnected() != mConnected.getAndSet(networkInfo.isConnected())) {
+                mListener.onConnectedChanged();
+            }
         }
 
         WifiConfiguration connectionConfig = null;
@@ -711,18 +721,25 @@
                 }
             }
 
-            if (reorder) Collections.sort(mInternalAccessPoints);
-            if (updated) mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED);
+            if (reorder) {
+                Collections.sort(mInternalAccessPoints);
+            }
+            if (updated) {
+                conditionallyNotifyListeners();
+            }
         }
     }
 
+    /**
+     * Clears the access point list and conditionally invokes
+     * {@link WifiListener#onAccessPointsChanged()} if required (i.e. the list was not already
+     * empty).
+     */
     private void clearAccessPointsAndConditionallyUpdate() {
         synchronized (mLock) {
             if (!mInternalAccessPoints.isEmpty()) {
                 mInternalAccessPoints.clear();
-                if (!mMainHandler.hasMessages(MainHandler.MSG_ACCESS_POINT_CHANGED)) {
-                    mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED);
-                }
+                mListener.onAccessPointsChanged();
             }
         }
     }
@@ -745,27 +762,26 @@
             }
             if (updated) {
                 Collections.sort(mInternalAccessPoints);
-                mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED);
+                conditionallyNotifyListeners();
             }
         }
     }
 
-    private void updateWifiState(int state) {
-        mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_WIFI_STATE, state, 0).sendToTarget();
-        if (!mWifiManager.isWifiEnabled()) {
-            clearAccessPointsAndConditionallyUpdate();
-        }
-    }
-
     @VisibleForTesting
     final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            // No work should be performed in this Receiver, instead all operations should be passed
+            // off to the WorkHandler to avoid concurrent modification exceptions.
+
             String action = intent.getAction();
 
             if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
-                updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                        WifiManager.WIFI_STATE_UNKNOWN));
+                mWorkHandler.obtainMessage(
+                        WorkHandler.MSG_UPDATE_WIFI_STATE,
+                        intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+                            WifiManager.WIFI_STATE_UNKNOWN),
+                        0).sendToTarget();
             } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) {
                 mWorkHandler
                         .obtainMessage(
@@ -778,12 +794,6 @@
                 mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
             } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
                 NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-
-                if(mConnected.get() != info.isConnected()) {
-                    mConnected.set(info.isConnected());
-                    mMainHandler.sendEmptyMessage(MainHandler.MSG_CONNECTED_CHANGED);
-                }
-
                 mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_NETWORK_INFO, info)
                         .sendToTarget();
                 mWorkHandler.sendEmptyMessage(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
@@ -808,68 +818,8 @@
     }
 
     @VisibleForTesting
-    final class MainHandler extends Handler {
-        @VisibleForTesting static final int MSG_CONNECTED_CHANGED = 0;
-        @VisibleForTesting static final int MSG_WIFI_STATE_CHANGED = 1;
-        @VisibleForTesting static final int MSG_ACCESS_POINT_CHANGED = 2;
-        private static final int MSG_RESUME_SCANNING = 3;
-        private static final int MSG_PAUSE_SCANNING = 4;
-
-        public MainHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            if (mListener == null) {
-                return;
-            }
-            switch (msg.what) {
-                case MSG_CONNECTED_CHANGED:
-                    mListener.onConnectedChanged();
-                    break;
-                case MSG_WIFI_STATE_CHANGED:
-                    mListener.onWifiStateChanged(msg.arg1);
-                    break;
-                case MSG_ACCESS_POINT_CHANGED:
-                    // Only notify listeners of changes if we have fresh scan results, otherwise the
-                    // UI will be updated with stale results. We want to copy the APs regardless,
-                    // for instances where forceUpdate was invoked by the caller.
-                    if (mStaleScanResults) {
-                        copyAndNotifyListeners(false /*notifyListeners*/);
-                    } else {
-                        copyAndNotifyListeners(true /*notifyListeners*/);
-                        mListener.onAccessPointsChanged();
-                    }
-                    break;
-                case MSG_RESUME_SCANNING:
-                    if (mScanner != null) {
-                        mScanner.resume();
-                    }
-                    break;
-                case MSG_PAUSE_SCANNING:
-                    if (mScanner != null) {
-                        mScanner.pause();
-                    }
-                    synchronized (mLock) {
-                        mStaleScanResults = true;
-                    }
-                    break;
-            }
-        }
-
-        void removePendingMessages() {
-            removeMessages(MSG_ACCESS_POINT_CHANGED);
-            removeMessages(MSG_CONNECTED_CHANGED);
-            removeMessages(MSG_WIFI_STATE_CHANGED);
-            removeMessages(MSG_PAUSE_SCANNING);
-            removeMessages(MSG_RESUME_SCANNING);
-        }
-    }
-
-    @VisibleForTesting
     final class WorkHandler extends Handler {
-        private static final int MSG_UPDATE_ACCESS_POINTS = 0;
+        @VisibleForTesting static final int MSG_UPDATE_ACCESS_POINTS = 0;
         private static final int MSG_UPDATE_NETWORK_INFO = 1;
         private static final int MSG_RESUME = 2;
         private static final int MSG_UPDATE_WIFI_STATE = 3;
@@ -882,6 +832,8 @@
 
         @Override
         public void handleMessage(Message msg) {
+            // TODO(sghuman): Clean up synchronization to only be used when modifying collections
+            // exposed to the MainThread (through onStart, onStop, forceUpdate).
             synchronized (mLock) {
                 processMessage(msg);
             }
@@ -911,6 +863,7 @@
                             mScanner.resume();
                         }
                     } else {
+                        clearAccessPointsAndConditionallyUpdate();
                         mLastInfo = null;
                         mLastNetworkInfo = null;
                         if (mScanner != null) {
@@ -920,8 +873,7 @@
                             mStaleScanResults = true;
                         }
                     }
-                    mMainHandler.obtainMessage(MainHandler.MSG_WIFI_STATE_CHANGED, msg.arg1, 0)
-                            .sendToTarget();
+                    mListener.onWifiStateChanged(msg.arg1);
                     break;
             }
         }
@@ -1010,16 +962,26 @@
 
         @Override
         public void onWifiStateChanged(int state) {
+            if (isVerboseLoggingEnabled()) {
+                Log.i(TAG,
+                        String.format("Invoking onWifiStateChanged callback with state %d", state));
+            }
             mHandler.post(() -> mDelegatee.onWifiStateChanged(state));
         }
 
         @Override
         public void onConnectedChanged() {
+            if (isVerboseLoggingEnabled()) {
+                Log.i(TAG, "Invoking onConnectedChanged callback");
+            }
             mHandler.post(() -> mDelegatee.onConnectedChanged());
         }
 
         @Override
         public void onAccessPointsChanged() {
+            if (isVerboseLoggingEnabled()) {
+                Log.i(TAG, "Invoking onAccessPointsChanged callback");
+            }
             mHandler.post(() -> mDelegatee.onAccessPointsChanged());
         }
     }
@@ -1041,101 +1003,27 @@
         void onWifiStateChanged(int state);
 
         /**
-         * Called when the connection state of wifi has changed and isConnected
-         * should be called to get the updated state.
+         * Called when the connection state of wifi has changed and
+         * {@link WifiTracker#isConnected()} should be called to get the updated state.
          */
         void onConnectedChanged();
 
         /**
          * Called to indicate the list of AccessPoints has been updated and
-         * getAccessPoints should be called to get the latest information.
+         * {@link WifiTracker#getAccessPoints()} should be called to get the updated list.
          */
         void onAccessPointsChanged();
     }
 
     /**
-     * Helps capture notifications that were generated during AccessPoint modification. Used later
-     * on by {@link #copyAndNotifyListeners(boolean)} to send notifications.
+     * Invokes {@link WifiListenerWrapper#onAccessPointsChanged()} if {@link #mStaleScanResults}
+     * is false.
      */
-    private static class AccessPointListenerAdapter implements AccessPoint.AccessPointListener {
-        static final int AP_CHANGED = 1;
-        static final int LEVEL_CHANGED = 2;
-
-        final SparseIntArray mPendingNotifications = new SparseIntArray();
-
-        @Override
-        public void onAccessPointChanged(AccessPoint accessPoint) {
-            int type = mPendingNotifications.get(accessPoint.mId);
-            mPendingNotifications.put(accessPoint.mId, type | AP_CHANGED);
+    private void conditionallyNotifyListeners() {
+        if (mStaleScanResults) {
+            return;
         }
 
-        @Override
-        public void onLevelChanged(AccessPoint accessPoint) {
-            int type = mPendingNotifications.get(accessPoint.mId);
-            mPendingNotifications.put(accessPoint.mId, type | LEVEL_CHANGED);
-        }
-    }
-
-    /**
-     * Responsible for copying access points from {@link #mInternalAccessPoints} and notifying
-     * accesspoint listeners.
-     *
-     * @param notifyListeners if true, accesspoint listeners are notified, otherwise notifications
-     *                        dropped.
-     */
-    @MainThread
-    private void copyAndNotifyListeners(boolean notifyListeners) {
-        // Need to watch out for memory allocations on main thread.
-        SparseArray<AccessPoint> oldAccessPoints = new SparseArray<>();
-        SparseIntArray notificationMap = null;
-        List<AccessPoint> updatedAccessPoints = new ArrayList<>();
-
-        for (AccessPoint accessPoint : mAccessPoints) {
-            oldAccessPoints.put(accessPoint.mId, accessPoint);
-        }
-
-        synchronized (mLock) {
-            if (DBG()) {
-                Log.d(TAG, "Starting to copy AP items on the MainHandler. Internal APs: "
-                        + mInternalAccessPoints);
-            }
-
-            if (notifyListeners) {
-                notificationMap = mAccessPointListenerAdapter.mPendingNotifications.clone();
-            }
-
-            mAccessPointListenerAdapter.mPendingNotifications.clear();
-
-            for (AccessPoint internalAccessPoint : mInternalAccessPoints) {
-                AccessPoint accessPoint = oldAccessPoints.get(internalAccessPoint.mId);
-                if (accessPoint == null) {
-                    accessPoint = new AccessPoint(mContext, internalAccessPoint);
-                } else {
-                    accessPoint.copyFrom(internalAccessPoint);
-                }
-                updatedAccessPoints.add(accessPoint);
-            }
-        }
-
-        mAccessPoints.clear();
-        mAccessPoints.addAll(updatedAccessPoints);
-
-        if (notificationMap != null && notificationMap.size() > 0) {
-            for (AccessPoint accessPoint : updatedAccessPoints) {
-                int notificationType = notificationMap.get(accessPoint.mId);
-                AccessPoint.AccessPointListener listener = accessPoint.mAccessPointListener;
-                if (notificationType == 0 || listener == null) {
-                    continue;
-                }
-
-                if ((notificationType & AccessPointListenerAdapter.AP_CHANGED) != 0) {
-                    listener.onAccessPointChanged(accessPoint);
-                }
-
-                if ((notificationType & AccessPointListenerAdapter.LEVEL_CHANGED) != 0) {
-                    listener.onLevelChanged(accessPoint);
-                }
-            }
-        }
+        ThreadUtils.postOnMainThread(() -> mListener.onAccessPointsChanged());
     }
 }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 1440311..54c02a2 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -113,38 +113,6 @@
     }
 
     @Test
-    public void testCopyAccessPoint_dataShouldMatch() {
-        WifiConfiguration configuration = createWifiConfiguration();
-        configuration.meteredHint = true;
-
-        NetworkInfo networkInfo =
-                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 2, "WIFI", "WIFI_SUBTYPE");
-        AccessPoint originalAccessPoint = new AccessPoint(mContext, configuration);
-        WifiInfo wifiInfo = new WifiInfo();
-        wifiInfo.setSSID(WifiSsid.createFromAsciiEncoded(configuration.SSID));
-        wifiInfo.setBSSID(configuration.BSSID);
-        originalAccessPoint.update(configuration, wifiInfo, networkInfo);
-        AccessPoint copy = new AccessPoint(mContext, originalAccessPoint);
-
-        assertThat(originalAccessPoint.getSsid().toString()).isEqualTo(copy.getSsid().toString());
-        assertThat(originalAccessPoint.getBssid()).isEqualTo(copy.getBssid());
-        assertThat(originalAccessPoint.getConfig()).isEqualTo(copy.getConfig());
-        assertThat(originalAccessPoint.getSecurity()).isEqualTo(copy.getSecurity());
-        assertThat(originalAccessPoint.isMetered()).isEqualTo(copy.isMetered());
-        assertThat(originalAccessPoint.compareTo(copy) == 0).isTrue();
-    }
-
-    @Test
-    public void testThatCopyAccessPoint_scanCacheShouldMatch() {
-        AccessPoint original = createAccessPointWithScanResultCache();
-        assertThat(original.getRssi()).isEqualTo(4);
-        AccessPoint copy = new AccessPoint(mContext, createWifiConfiguration());
-        assertThat(copy.getRssi()).isEqualTo(AccessPoint.UNREACHABLE_RSSI);
-        copy.copyFrom(original);
-        assertThat(original.getRssi()).isEqualTo(copy.getRssi());
-    }
-
-    @Test
     public void testCompareTo_GivesActiveBeforeInactive() {
         AccessPoint activeAp = new TestAccessPointBuilder(mContext).setActive(true).build();
         AccessPoint inactiveAp = new TestAccessPointBuilder(mContext).setActive(false).build();
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 6be4936..0c49bb6 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -356,19 +356,14 @@
 
     private void waitForHandlersToProcessCurrentlyEnqueuedMessages(WifiTracker tracker)
             throws InterruptedException {
+        // TODO(sghuman): This should no longer be necessary in a single work handler model
+
         CountDownLatch workerLatch = new CountDownLatch(1);
         tracker.mWorkHandler.post(() -> {
             workerLatch.countDown();
         });
         assertTrue("Latch timed out while waiting for WorkerHandler",
                 workerLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
-
-        CountDownLatch mainLatch = new CountDownLatch(1);
-        tracker.mMainHandler.post(() -> {
-            mainLatch.countDown();
-        });
-        assertTrue("Latch timed out while waiting for MainHandler",
-                mainLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
     }
 
     private void switchToNetwork2(WifiTracker tracker) throws InterruptedException {
@@ -390,38 +385,6 @@
     }
 
     @Test
-    public void testAccessPointListenerSetWhenLookingUpUsingScanResults() {
-        ScanResult scanResult = new ScanResult();
-        scanResult.level = 123;
-        scanResult.BSSID = "bssid-" + 111;
-        scanResult.timestamp = SystemClock.elapsedRealtime() * 1000;
-        scanResult.capabilities = "";
-
-        WifiTracker tracker = new WifiTracker(
-                InstrumentationRegistry.getTargetContext(), null, true, true);
-
-        AccessPoint result = tracker.getCachedOrCreate(
-                Collections.singletonList(scanResult), new ArrayList<AccessPoint>());
-        assertTrue(result.mAccessPointListener != null);
-    }
-
-    @Test
-    public void testAccessPointListenerSetWhenLookingUpUsingWifiConfiguration() {
-        WifiConfiguration configuration = new WifiConfiguration();
-        configuration.SSID = "test123";
-        configuration.BSSID="bssid";
-        configuration.networkId = 123;
-        configuration.allowedKeyManagement = new BitSet();
-        configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
-
-        WifiTracker tracker = new WifiTracker(
-                InstrumentationRegistry.getTargetContext(), null, true, true);
-
-        AccessPoint result = tracker.getCachedOrCreate(configuration, new ArrayList<AccessPoint>());
-        assertTrue(result.mAccessPointListener != null);
-    }
-
-    @Test
     public void startAndStopTrackingShouldRegisterAndUnregisterScoreCache()
             throws InterruptedException {
         WifiTracker tracker = createMockedWifiTracker();
@@ -534,7 +497,6 @@
         waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
     }
 
-    @FlakyTest
     @Test
     public void scoreCacheUpdateScoresShouldChangeSortOrder() throws InterruptedException {
         WifiTracker tracker =  createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
@@ -634,9 +596,9 @@
     public void scoresShouldBeRequestedForNewScanResultOnly()  throws InterruptedException {
         // Scores can be requested together or serially depending on how the scan results are
         // processed.
-        mRequestScoresLatch = new CountDownLatch(2);
+        mRequestScoresLatch = new CountDownLatch(1);
         WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
-        mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
+        assertTrue(mRequestScoresLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
         mRequestedKeys.clear();
 
         String ssid = "ssid3";
@@ -770,7 +732,7 @@
         CountDownLatch ready = new CountDownLatch(1);
         CountDownLatch latch = new CountDownLatch(1);
         CountDownLatch lock = new CountDownLatch(1);
-        tracker.mMainHandler.post(() -> {
+        tracker.mWorkHandler.post(() -> {
             try {
                 ready.countDown();
                 lock.await();
@@ -781,12 +743,7 @@
         });
 
         // Enqueue messages
-        tracker.mMainHandler.sendEmptyMessage(
-                WifiTracker.MainHandler.MSG_ACCESS_POINT_CHANGED);
-        tracker.mMainHandler.sendEmptyMessage(
-                WifiTracker.MainHandler.MSG_CONNECTED_CHANGED);
-        tracker.mMainHandler.sendEmptyMessage(
-                WifiTracker.MainHandler.MSG_WIFI_STATE_CHANGED);
+        tracker.mWorkHandler.sendEmptyMessage(WifiTracker.WorkHandler.MSG_UPDATE_ACCESS_POINTS);
 
         try {
             ready.await(); // Make sure we have entered the first message handler
@@ -800,12 +757,9 @@
         lock.countDown();
         assertTrue("Latch timed out", latch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
 
-        assertThat(tracker.mMainHandler.hasMessages(
-                WifiTracker.MainHandler.MSG_ACCESS_POINT_CHANGED)).isFalse();
-        assertThat(tracker.mMainHandler.hasMessages(
-                WifiTracker.MainHandler.MSG_CONNECTED_CHANGED)).isFalse();
-        assertThat(tracker.mMainHandler.hasMessages(
-                WifiTracker.MainHandler.MSG_WIFI_STATE_CHANGED)).isFalse();
+        assertThat(tracker.mWorkHandler.hasMessages(
+                WifiTracker.WorkHandler.MSG_UPDATE_ACCESS_POINTS)).isFalse();
+        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
 
         verifyNoMoreInteractions(mockWifiListener);
     }
@@ -862,7 +816,7 @@
         mAccessPointsChangedLatch = new CountDownLatch(1);
         tracker.mReceiver.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION));
 
-        mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
+        assertTrue(mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
         waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
 
         assertThat(tracker.getAccessPoints()).isEmpty();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 12d3106..706d0c0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -16,6 +16,9 @@
 package com.android.settingslib;
 
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+
+import static com.android.settingslib.Utils.STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY;
+
 import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Matchers.eq;
@@ -30,6 +33,7 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.location.LocationManager;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
@@ -136,7 +140,7 @@
     }
 
     @Test
-    public void testStorageManagerDaysToRetainUsesResources() {
+    public void testGetDefaultStorageManagerDaysToRetain_storageManagerDaysToRetainUsesResources() {
         Resources resources = mock(Resources.class);
         when(resources.getInteger(
                         eq(
@@ -149,6 +153,12 @@
         assertThat(Utils.getDefaultStorageManagerDaysToRetain(resources)).isEqualTo(60);
     }
 
+    @Test
+    public void testIsStorageManagerEnabled_UsesSystemProperties() {
+        SystemProperties.set(STORAGE_MANAGER_SHOW_OPT_IN_PROPERTY, "false");
+        assertThat(Utils.isStorageManagerEnabled(mContext)).isTrue();
+    }
+
     private static ArgumentMatcher<Intent> actionMatches(String expected) {
         return intent -> TextUtils.equals(expected, intent.getAction());
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
index 9b5da4a..ccd2f53 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java
@@ -17,15 +17,22 @@
 package com.android.settingslib.notification;
 
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.app.Fragment;
+import android.app.NotificationManager;
 import android.content.Context;
+import android.content.res.Resources;
 import android.net.Uri;
 import android.service.notification.Condition;
 import android.view.LayoutInflater;
@@ -46,7 +53,11 @@
     @Mock
     private Context mContext;
     @Mock
+    private Resources mResources;
+    @Mock
     private Fragment mFragment;
+    @Mock
+    private NotificationManager mNotificationManager;
 
     private Context mShadowContext;
     private LayoutInflater mLayoutInflater;
@@ -58,6 +69,7 @@
         MockitoAnnotations.initMocks(this);
         mShadowContext = RuntimeEnvironment.application;
         when(mContext.getApplicationContext()).thenReturn(mContext);
+        when(mContext.getResources()).thenReturn(mResources);
         when(mFragment.getContext()).thenReturn(mShadowContext);
         mLayoutInflater = LayoutInflater.from(mShadowContext);
 
@@ -67,6 +79,10 @@
         mController.mForeverId =  Condition.newId(mContext).appendPath("forever").build();
         when(mContext.getString(com.android.internal.R.string.zen_mode_forever))
                 .thenReturn("testSummary");
+        NotificationManager.Policy alarmsEnabledPolicy = new NotificationManager.Policy(
+                NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS, 0, 0, 0);
+        doReturn(alarmsEnabledPolicy).when(mNotificationManager).getNotificationPolicy();
+        mController.mNotificationManager = mNotificationManager;
         mController.getContentView();
 
         // these methods use static calls to ZenModeConfig which would normally fail in robotests,
@@ -141,4 +157,38 @@
         assertFalse(mController.getConditionTagAt(
                 EnableZenModeDialog.COUNTDOWN_ALARM_CONDITION_INDEX).rb.isChecked());
     }
+
+    @Test
+    public void testNoAlarmWarning() {
+        // setup alarm
+        long now = System.currentTimeMillis();
+        doReturn(now + 100000).when(mController).getNextAlarm();
+        doReturn("").when(mController).getTime(anyLong(), anyLong());
+
+        // allow alarms
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(
+                new NotificationManager.Policy(
+                        NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS, 0, 0, 0));
+
+        // alarm warning should be null
+        assertNull(mController.computeAlarmWarningText(null));
+    }
+
+    @Test
+    public void testAlarmWarning() {
+        // setup alarm
+        long now = System.currentTimeMillis();
+        doReturn(now + 1000000).when(mController).getNextAlarm();
+        doReturn("").when(mController).getTime(anyLong(), anyLong());
+
+        // don't allow alarms to bypass dnd
+        when(mNotificationManager.getNotificationPolicy()).thenReturn(
+                new NotificationManager.Policy(0, 0, 0, 0));
+
+        // return a string if mResources is asked to retrieve a string
+        when(mResources.getString(anyInt(), anyString())).thenReturn("");
+
+        // alarm warning should NOT be null
+        assertNotNull(mController.computeAlarmWarningText(null));
+    }
 }
\ No newline at end of file
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 91957e1..ad422d8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -76,11 +76,10 @@
      */
     private static final ArraySet<String> sBroadcastOnRestore;
     static {
-        sBroadcastOnRestore = new ArraySet<String>(5);
+        sBroadcastOnRestore = new ArraySet<String>(4);
         sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
         sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS);
         sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
-        sBroadcastOnRestore.add(Settings.Secure.ENABLED_INPUT_METHODS);
         sBroadcastOnRestore.add(Settings.Global.BLUETOOTH_ON);
     }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 769b7e9..1e8c523 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -870,6 +870,12 @@
         dumpSetting(s, p,
                 Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING,
                 GlobalSettingsProto.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING);
+        dumpSetting(s, p,
+                Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT,
+                GlobalSettingsProto.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT);
+        dumpSetting(s, p,
+                Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS,
+                GlobalSettingsProto.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS);
         // Settings.Global.SHOW_PROCESSES intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Global.LOW_POWER_MODE,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 85a579d..87ea382 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1591,6 +1591,7 @@
     private boolean isGlobalOrSecureSettingRestrictedForUser(String setting, int userId,
             String value, int callingUid) {
         String restriction;
+        boolean checkAllUser = false;
         switch (setting) {
             case Settings.Secure.LOCATION_MODE:
                 // Note LOCATION_MODE will be converted into LOCATION_PROVIDERS_ALLOWED
@@ -1656,6 +1657,12 @@
                 restriction = UserManager.DISALLOW_AMBIENT_DISPLAY;
                 break;
 
+            case Global.LOCATION_GLOBAL_KILL_SWITCH:
+                if ("0".equals(value)) return false;
+                restriction = UserManager.DISALLOW_CONFIG_LOCATION;
+                checkAllUser = true;
+                break;
+
             default:
                 if (setting != null && setting.startsWith(Settings.Global.DATA_ROAMING)) {
                     if ("0".equals(value)) return false;
@@ -1665,7 +1672,11 @@
                 return false;
         }
 
-        return mUserManager.hasUserRestriction(restriction, UserHandle.of(userId));
+        if (checkAllUser) {
+            return mUserManager.hasUserRestrictionOnAnyUser(restriction);
+        } else {
+            return mUserManager.hasUserRestriction(restriction, UserHandle.of(userId));
+        }
     }
 
     private int resolveOwningUserIdForSecureSettingLocked(int userId, String setting) {
diff --git a/packages/SimAppDialog/Android.mk b/packages/SimAppDialog/Android.mk
new file mode 100644
index 0000000..00a2e60
--- /dev/null
+++ b/packages/SimAppDialog/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := SimAppDialog
+LOCAL_CERTIFICATE := platform
+
+
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+    android-support-v4
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+include frameworks/opt/setupwizard/library/common-platform-deprecated.mk
+
+include $(BUILD_PACKAGE)
diff --git a/packages/SimAppDialog/AndroidManifest.xml b/packages/SimAppDialog/AndroidManifest.xml
new file mode 100644
index 0000000..873f6c5
--- /dev/null
+++ b/packages/SimAppDialog/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.simappdialog">
+    <application android:label="@string/app_name">
+        <activity
+            android:name=".InstallCarrierAppActivity"
+            android:exported="true"
+            android:permission="android.permission.NETWORK_SETTINGS"
+            android:theme="@style/SuwThemeGlif.Light">
+        </activity>
+    </application>
+</manifest>
diff --git a/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded_24px.xml b/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded_24px.xml
new file mode 100644
index 0000000..85896e8
--- /dev/null
+++ b/packages/SimAppDialog/res/drawable/ic_signal_cellular_alt_rounded_24px.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="21dp"
+        android:height="22dp"
+        android:viewportWidth="21"
+        android:viewportHeight="22">
+
+    <group
+        android:translateX="-196.000000"
+        android:translateY="-77.000000">
+        <group
+            android:translateX="190.000000"
+            android:translateY="72.000000">
+            <path
+                android:fillType="evenOdd"
+                android:strokeWidth="1"
+                android:pathData="M 0 0 H 32 V 32 H 0 V 0 Z"/>
+            <group
+                android:translateX="6.666667"
+                android:translateY="5.333333">
+                <path
+                    android:fillColor="#4285F4"
+                    android:strokeWidth="1"
+                    android:pathData="M 17 0 L 19 0 Q 20 0 20 1 L 20 20.3333333 Q 20 21.3333333 19 21.3333333 L 17 21.3333333 Q 16 21.3333333 16 20.3333333 L 16 1 Q 16 0 17 0 Z"/>
+                <path
+                    android:fillColor="#4285F4"
+                    android:strokeWidth="1"
+                    android:pathData="M 1 13.3333333 L 3 13.3333333 Q 4 13.3333333 4 14.3333333 L 4 20.3333333 Q 4 21.3333333 3 21.3333333 L 1 21.3333333 Q 0 21.3333333 0 20.3333333 L 0 14.3333333 Q 0 13.3333333 1 13.3333333 Z"/>
+                <path
+                    android:fillColor="#4285F4"
+                    android:strokeWidth="1"
+                    android:pathData="M 9 6.66666667 L 11 6.66666667 Q 12 6.66666667 12 7.66666667 L 12 20.33333337 Q 12 21.33333337 11 21.33333337 L 9 21.33333337 Q 8 21.33333337 8 20.33333337 L 8 7.66666667 Q 8 6.66666667 9 6.66666667 Z"/>
+            </group>
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SimAppDialog/res/drawable/placeholder.xml b/packages/SimAppDialog/res/drawable/placeholder.xml
new file mode 100644
index 0000000..53eee74
--- /dev/null
+++ b/packages/SimAppDialog/res/drawable/placeholder.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 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.
+-->
+<!-- TODO(b/72511181): replace when illustration is finished -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="270dp"
+        android:height="270dp"
+        android:viewportHeight="270.0"
+        android:viewportWidth="270.0">
+    <path android:fillColor="#E8EAED"
+          android:pathData="M183.54,265H84.88c-7.63,0 -13.81,-6.18 -13.81,-13.81V18.81C71.07,11.18 77.25,5 84.88,5h98.66c7.63,0 13.81,6.18 13.81,13.81v232.38C197.35,258.82 191.17,265 183.54,265z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M183.54,6.63c6.72,0 12.18,5.46 12.18,12.18v232.38c0,6.72 -5.46,12.18 -12.18,12.18H84.88c-6.72,0 -12.18,-5.46 -12.18,-12.18V18.81c0,-6.72 5.46,-12.18 12.18,-12.18H183.54M183.54,5H84.88c-7.63,0 -13.81,6.18 -13.81,13.81v232.38c0,7.63 6.18,13.81 13.81,13.81h98.66c7.63,0 13.81,-6.18 13.81,-13.81V18.81C197.35,11.18 191.17,5 183.54,5L183.54,5z"/>
+    <path android:fillColor="#FFFFFF"
+          android:pathData="M186.34,243.74H82.08c-2.41,0 -4.36,-1.95 -4.36,-4.36V30.61c0,-2.41 1.95,-4.36 4.36,-4.36h104.26c2.41,0 4.36,1.95 4.36,4.36v208.78C190.7,241.79 188.75,243.74 186.34,243.74z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M156.07,254.78h-43.72c-0.65,0 -1.18,-0.53 -1.18,-1.18v-0.08c0,-0.65 0.53,-1.18 1.18,-1.18h43.72c0.65,0 1.18,0.53 1.18,1.18v0.08C157.25,254.25 156.72,254.78 156.07,254.78z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M156.07,17.67h-43.72c-0.65,0 -1.18,-0.53 -1.18,-1.18V16.4c0,-0.65 0.53,-1.18 1.18,-1.18l43.72,0c0.65,0 1.18,0.53 1.18,1.18v0.08C157.25,17.14 156.72,17.67 156.07,17.67z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M197.85,84.16h-0.5V67.51h0.5c0.6,0 1.08,0.48 1.08,1.08v14.5C198.93,83.68 198.45,84.16 197.85,84.16z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M197.41,136.45h-0.06v-32.87h0.06c0.84,0 1.52,0.68 1.52,1.52v29.84C198.93,135.77 198.25,136.45 197.41,136.45z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M119.3,74.73l2.71,2.71c6.74,-6.74 17.67,-6.74 24.4,0l2.71,-2.71C140.89,66.49 127.54,66.49 119.3,74.73zM130.15,85.57l4.07,4.07l4.07,-4.07C136.04,83.33 132.39,83.33 130.15,85.57zM124.72,80.15l2.71,2.71c3.74,-3.74 9.82,-3.74 13.56,0l2.71,-2.71C138.46,74.91 129.96,74.91 124.72,80.15z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M143.7,179h-1.36v-2.71h-2.71V179h-10.85v-2.71h-2.71V179h-1.36c-1.5,0 -2.7,1.21 -2.7,2.71l-0.01,18.98c0,1.5 1.21,2.71 2.71,2.71h18.98c1.5,0 2.71,-1.21 2.71,-2.71v-18.98C146.41,180.22 145.2,179 143.7,179zM143.7,200.7h-18.98v-14.91h18.98V200.7zM127.43,188.49h6.78v6.78h-6.78V188.49z"/>
+    <path android:fillColor="#BDC1C6"
+          android:pathData="M146.41,144.49v-18.98c0,-1.5 -1.21,-2.71 -2.71,-2.71h-18.98c-1.5,0 -2.71,1.21 -2.71,2.71v18.98c0,1.5 1.21,2.71 2.71,2.71h18.98C145.2,147.2 146.41,145.99 146.41,144.49zM129.47,137.03l3.39,4.07l4.75,-6.11l6.1,8.13h-18.98L129.47,137.03z"/>
+</vector>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
new file mode 100644
index 0000000..0462a93
--- /dev/null
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 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.
+-->
+<com.android.setupwizardlib.GlifLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/setup_wizard_layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:icon="@drawable/ic_signal_cellular_alt_rounded_24px"
+    app:suwHeaderText="@string/install_carrier_app_title"
+    app:suwFooter="@layout/install_carrier_app_footer">
+
+    <LinearLayout
+        style="@style/SuwContentFrame"
+        android:id="@+id/content_frame"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/install_carrier_app_description"
+            style="@style/SuwDescription.Glif"
+            android:text="@string/install_carrier_app_description_default"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"/>
+
+        <com.android.setupwizardlib.view.FillContentLayout
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1">
+
+            <!-- TODO(b/72511181): final illo and content description update -->
+            <ImageView
+                android:src="@drawable/placeholder"
+                style="@style/SuwContentIllustration"
+                android:contentDescription="@null"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"/>
+
+        </com.android.setupwizardlib.view.FillContentLayout>
+    </LinearLayout>
+
+</com.android.setupwizardlib.GlifLayout>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
new file mode 100644
index 0000000..10dcb77
--- /dev/null
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 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.
+-->
+
+<com.android.setupwizardlib.view.ButtonBarLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/footer"
+    style="@style/SuwGlifButtonBar.Stackable"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <Button
+        android:id="@+id/skip_button"
+        style="@style/SuwGlifButton.Secondary"
+        android:text="@string/install_carrier_app_defer_action"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+    <Space
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
+
+    <Button
+        android:id="@+id/download_button"
+        style="@style/SuwGlifButton.Primary"
+        android:text="@string/install_carrier_app_download_action"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+</com.android.setupwizardlib.view.ButtonBarLayout>
diff --git a/packages/SimAppDialog/res/values/strings.xml b/packages/SimAppDialog/res/values/strings.xml
new file mode 100644
index 0000000..0c3930d
--- /dev/null
+++ b/packages/SimAppDialog/res/values/strings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- TODO character limits -->
+    <!-- The name of this application -->
+    <string name="app_name">Sim App Dialog</string>
+    <!-- Install Carrier App Activity -->
+    <!-- Title of screen asking user to download the carrier app to match the inserted SIM card -->
+    <string name="install_carrier_app_title">Activate mobile service</string>
+    <!-- Description of screen asking user to download the carrier app to match the inserted SIM card if we know the name of the carrier-->
+    <string name="install_carrier_app_description">To get your new SIM working properly, you\'ll
+        need to install the <xliff:g name="carrier_name" example="Project Fi">%1$s</xliff:g> app
+    </string>
+    <!-- Description of screen asking user to download the carrier app to match the inserted SIM card if we don't know the name of the carrier-->
+    <string name="install_carrier_app_description_default">To get your new SIM working properly,
+        you\'ll need to install the carrier app
+    </string>
+    <!-- Name of the button used to defer downloading the carrier app -->
+    <string name="install_carrier_app_defer_action">Not now</string>
+    <!-- Name of the button for downloading the carrier app -->
+    <string name="install_carrier_app_download_action">Download app</string>
+</resources>
\ No newline at end of file
diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
new file mode 100644
index 0000000..9e9b80d
--- /dev/null
+++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 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 com.android.simappdialog;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.setupwizardlib.util.WizardManagerHelper;
+
+/**
+ * Activity that gives a user the choice to download the SIM app or defer until a later time
+ *
+ * Will finish with result {@link #DEFER_RESULT} on defer button press or {@link #DOWNLOAD_RESULT}
+ * if the download button is pressed
+ *
+ * Can display the carrier app name if its passed into the intent with key
+ * {@link #BUNDLE_KEY_CARRIER_NAME}
+ */
+public class InstallCarrierAppActivity extends Activity implements View.OnClickListener {
+    /**
+     * Key for the carrier app name that will be displayed as the app to download.  If unset, a
+     * default description will be used
+     */
+    public static final String BUNDLE_KEY_CARRIER_NAME = "carrier_name";
+    /** Result code when the defer button is pressed */
+    public static final int DEFER_RESULT = 1;
+    /** Result code when the download button is pressed */
+    public static final int DOWNLOAD_RESULT = 2;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        // Setup theme for aosp/pixel
+        setTheme(
+                WizardManagerHelper.getThemeRes(
+                        SystemProperties.get("setupwizard.theme"),
+                        R.style.SuwThemeGlif_Light
+                )
+        );
+
+        super.onCreate(icicle);
+        setContentView(R.layout.install_carrier_app_activity);
+
+        Button notNowButton = findViewById(R.id.skip_button);
+        notNowButton.setOnClickListener(this);
+
+        Button downloadButton = findViewById(R.id.download_button);
+        downloadButton.setOnClickListener(this);
+
+        // Include carrier name in description text if its present in the intent
+        Intent intent = getIntent();
+        if (intent != null) {
+            String carrierName = intent.getStringExtra(BUNDLE_KEY_CARRIER_NAME);
+            if (!TextUtils.isEmpty(carrierName)) {
+                TextView subtitle = findViewById(R.id.install_carrier_app_description);
+                subtitle.setText(getString(R.string.install_carrier_app_description, carrierName));
+            }
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        switch (v.getId()) {
+            case R.id.skip_button:
+                finish(DEFER_RESULT);
+                break;
+            case R.id.download_button:
+                finish(DOWNLOAD_RESULT);
+                break;
+        }
+    }
+
+    private void finish(int resultCode) {
+        setResult(resultCode);
+        finish();
+    }
+}
diff --git a/packages/SystemUI/res/drawable/car_ic_hvac.xml b/packages/SystemUI/res/drawable/car_ic_hvac.xml
new file mode 100644
index 0000000..bdc44b3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_ic_hvac.xml
@@ -0,0 +1,51 @@
+<!--
+    Copyright (C) 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="37dp"
+    android:height="31dp"
+    android:viewportWidth="37"
+    android:viewportHeight="31">
+
+    <group
+            android:translateX="-4.000000"
+            android:translateY="-6.000000">
+        <group
+                android:translateX="5.000000"
+                android:translateY="5.000000">
+            <path
+                android:fillType="evenOdd"
+                android:strokeColor="#FAFAFA"
+                android:strokeWidth="3.5"
+                android:pathData="M0.320769938,6.07518051 C6.46754647,1.46509811 12.4222362,1.46509811
+18.1848392,6.07518051 C23.9474422,10.6852629 29.3258717,10.4931761
+34.3201276,5.49892021" />
+            <path
+                android:fillType="evenOdd"
+                android:strokeColor="#FAFAFA"
+                android:strokeWidth="3.5"
+                android:pathData="M0.320769938,17.0751805 C6.46754647,12.4650981 12.4222362,12.4650981
+18.1848392,17.0751805 C23.9474422,21.6852629 29.3258717,21.4931761
+34.3201276,16.4989202" />
+            <path
+                android:fillType="evenOdd"
+                android:strokeColor="#FAFAFA"
+                android:strokeWidth="3.5"
+                android:pathData="M0.320769938,28.0751805 C6.46754647,23.4650981 12.4222362,23.4650981
+18.1848392,28.0751805 C23.9474422,32.6852629 29.3258717,32.4931761
+34.3201276,27.4989202" />
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_notification.xml b/packages/SystemUI/res/drawable/car_ic_notification.xml
new file mode 100644
index 0000000..61d937b90
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_ic_notification.xml
@@ -0,0 +1,28 @@
+<!--
+    Copyright (C) 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="56dp"
+    android:height="56dp"
+    android:viewportWidth="48"
+    android:viewportHeight="48">
+
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M24 44c2.21 0 4-1.79 4-4h-8c0 2.21 1.79 4 4
+4zm12-12V22c0-6.15-3.27-11.28-9-12.64V8c0-1.66-1.34-3-3-3s-3 1.34-3 3v1.36c-5.73
+1.36-9 6.49-9 12.64v10l-4 4v2h32v-2l-4-4zm-4 2H16V22c0-4.97 3.03-9 8-9s8 4.03 8
+9v12z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_overview.xml b/packages/SystemUI/res/drawable/car_ic_overview.xml
new file mode 100644
index 0000000..4651dcb
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_ic_overview.xml
@@ -0,0 +1,28 @@
+<!--
+    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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="56dp"
+    android:height="56dp"
+    android:viewportWidth="48"
+    android:viewportHeight="48">
+
+    <path
+        android:pathData="M0 0h48v48H0z" />
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M24 4C12.95 4 4 12.95 4 24s8.95 20 20 20 20-8.95 20-20S35.05 4 24 4zm0 36c-8.82
+0-16-7.18-16-16S15.18 8 24 8s16 7.18 16 16-7.18 16-16 16z" />
+</vector>
diff --git a/packages/SystemUI/res/layout/car_facet_button.xml b/packages/SystemUI/res/layout/car_facet_button.xml
new file mode 100644
index 0000000..f432d36
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_facet_button.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2017, 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.
+*/
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+    <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/car_facet_button"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="center"
+        android:animateLayoutChanges="true"
+        android:orientation="vertical">
+
+        <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/car_nav_button_icon"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:animateLayoutChanges="true"
+            android:background="@android:color/transparent"
+            android:scaleType="fitCenter">
+        </com.android.keyguard.AlphaOptimizedImageButton>
+
+        <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/car_nav_button_more_icon"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:animateLayoutChanges="true"
+            android:background="@android:color/transparent"
+            android:scaleType="fitCenter">
+        </com.android.keyguard.AlphaOptimizedImageButton>
+
+    </LinearLayout>
+</merge>
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar.xml b/packages/SystemUI/res/layout/car_left_navigation_bar.xml
new file mode 100644
index 0000000..866b5a5
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_left_navigation_bar.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:orientation="vertical"
+    android:background="@drawable/system_bar_background">
+
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:id="@+id/nav_buttons"
+        android:orientation="vertical"
+        android:gravity="top"
+        android:paddingTop="30dp"
+        android:layout_weight="1"
+        android:background="@drawable/system_bar_background"
+        android:animateLayoutChanges="true">
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/home"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+            android:src="@drawable/car_ic_overview"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="30dp"
+            android:paddingBottom="30dp"
+        />
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/hvac"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end"
+            systemui:broadcast="true"
+            android:src="@drawable/car_ic_hvac"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="30dp"
+            android:paddingBottom="30dp"
+        />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="bottom"
+        android:orientation="vertical">
+
+        <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/notifications"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:src="@drawable/car_ic_notification"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="20dp"
+            android:paddingBottom="20dp"
+            android:alpha="0.7"
+        />
+
+        <com.android.systemui.statusbar.policy.Clock
+            android:id="@+id/clock"
+            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:singleLine="true"
+            android:paddingStart="@dimen/status_bar_clock_starting_padding"
+            android:paddingEnd="@dimen/status_bar_clock_end_padding"
+            android:gravity="center_horizontal"
+            android:paddingBottom="20dp"
+        />
+
+        <Space
+            android:layout_height="10dp"
+            android:layout_width="match_parent"/>
+
+    </LinearLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/car_navigation_bar.xml b/packages/SystemUI/res/layout/car_navigation_bar.xml
index 999dbac..4666c60 100644
--- a/packages/SystemUI/res/layout/car_navigation_bar.xml
+++ b/packages/SystemUI/res/layout/car_navigation_bar.xml
@@ -19,34 +19,80 @@
 
 <com.android.systemui.statusbar.car.CarNavigationBarView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:layout_height="match_parent"
     android:layout_width="match_parent"
-    android:gravity="center"
     android:background="@drawable/system_bar_background">
 
-    <!-- phone.NavigationBarView has rot0 and rot90 but we expect the car head unit to have a fixed
-         rotation so skip this level of the heirarchy.
-    -->
     <LinearLayout
         android:layout_height="match_parent"
-        android:layout_width="@dimen/car_navigation_bar_width"
+        android:layout_width="wrap_content"
         android:orientation="horizontal"
-        android:clipChildren="false"
-        android:clipToPadding="false"
         android:id="@+id/nav_buttons"
+        android:gravity="left"
+        android:paddingLeft="30dp"
+        android:layout_weight="1"
         android:animateLayoutChanges="true">
 
-        <!-- Buttons get populated here from a car_arrays.xml. -->
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/home"
+            android:layout_height="match_parent"
+            android:layout_width="wrap_content"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+            android:src="@drawable/car_ic_overview"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingLeft="30dp"
+            android:paddingRight="30dp"
+        />
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/hvac"
+            android:layout_height="match_parent"
+            android:layout_width="wrap_content"
+            systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end"
+            systemui:broadcast="true"
+            android:src="@drawable/car_ic_hvac"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingLeft="30dp"
+            android:paddingRight="30dp"
+        />
     </LinearLayout>
 
-    <!-- lights out layout to match exactly -->
     <LinearLayout
+        android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:layout_width="match_parent"
-        android:orientation="horizontal"
-        android:id="@+id/lights_out"
-        android:visibility="gone">
-        <!-- Must match nav_buttons. -->
+        android:layout_weight="1"
+        android:gravity="right"
+        android:orientation="horizontal">
+
+        <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/notifications"
+            android:layout_height="match_parent"
+            android:layout_width="wrap_content"
+            android:src="@drawable/car_ic_notification"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingLeft="20dp"
+            android:paddingRight="20dp"
+            android:alpha="0.7"
+        />
+
+        <com.android.systemui.statusbar.policy.Clock
+            android:id="@+id/clock"
+            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:singleLine="true"
+            android:paddingStart="@dimen/status_bar_clock_starting_padding"
+            android:paddingEnd="@dimen/status_bar_clock_end_padding"
+            android:gravity="center_vertical"
+            android:paddingRight="20dp"
+        />
+
+        <Space
+            android:layout_width="10dp"
+            android:layout_height="match_parent"/>
+
     </LinearLayout>
 
 </com.android.systemui.statusbar.car.CarNavigationBarView>
+
diff --git a/packages/SystemUI/res/layout/car_navigation_button.xml b/packages/SystemUI/res/layout/car_navigation_button.xml
index 7677646..4062eb8 100644
--- a/packages/SystemUI/res/layout/car_navigation_button.xml
+++ b/packages/SystemUI/res/layout/car_navigation_button.xml
@@ -17,28 +17,13 @@
 */
 -->
 
-<com.android.systemui.statusbar.car.CarNavigationButton
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_height="match_parent"
-        android:layout_width="wrap_content"
-        android:orientation="horizontal"
-        android:background="?android:attr/selectableItemBackground">
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
     <com.android.keyguard.AlphaOptimizedImageButton
-            android:id="@+id/car_nav_button_icon"
-            android:layout_height="match_parent"
-            android:layout_width="@dimen/car_navigation_button_width"
-            android:layout_centerInParent="true"
-            android:animateLayoutChanges="true"
-            android:scaleType="fitCenter">
+        android:id="@+id/car_nav_button_icon"
+        android:layout_height="wrap_content"
+        android:layout_width="@dimen/car_navigation_button_width"
+        android:layout_centerInParent="true"
+        android:animateLayoutChanges="true"
+        android:scaleType="fitCenter">
     </com.android.keyguard.AlphaOptimizedImageButton>
-
-    <com.android.keyguard.AlphaOptimizedImageButton
-            android:id="@+id/car_nav_button_more_icon"
-            android:layout_height="match_parent"
-            android:layout_width="wrap_content"
-            android:layout_centerVertical="true"
-            android:layout_toRightOf="@+id/car_nav_button_icon"
-            android:animateLayoutChanges="true"
-            android:scaleType="fitCenter">
-    </com.android.keyguard.AlphaOptimizedImageButton>
-</com.android.systemui.statusbar.car.CarNavigationButton>
+</merge>
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar.xml b/packages/SystemUI/res/layout/car_right_navigation_bar.xml
new file mode 100644
index 0000000..99ab802
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_right_navigation_bar.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:orientation="vertical"
+    android:background="@drawable/system_bar_background">
+
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:id="@+id/nav_buttons"
+        android:orientation="vertical"
+        android:gravity="top"
+        android:paddingTop="30dp"
+        android:layout_weight="1"
+        android:background="@drawable/system_bar_background"
+        android:animateLayoutChanges="true">
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/home"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+            android:src="@drawable/car_ic_overview"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="30dp"
+            android:paddingBottom="30dp"
+        />
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/hvac"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            systemui:intent="intent:#Intent;action=android.car.intent.action.SHOW_HVAC_CONTROLS;end"
+            systemui:broadcast="true"
+            android:src="@drawable/car_ic_hvac"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="30dp"
+            android:paddingBottom="30dp"
+        />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="bottom"
+        android:orientation="vertical">
+
+        <com.android.keyguard.AlphaOptimizedImageButton
+            android:id="@+id/notifications"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:src="@drawable/car_ic_notification"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="20dp"
+            android:paddingBottom="20dp"
+            android:alpha="0.7"
+        />
+
+
+        <com.android.systemui.statusbar.policy.Clock
+            android:id="@+id/clock"
+            android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:singleLine="true"
+            android:paddingStart="@dimen/status_bar_clock_starting_padding"
+            android:paddingEnd="@dimen/status_bar_clock_end_padding"
+            android:gravity="center_horizontal"
+            android:paddingBottom="20dp"
+        />
+
+        <Space
+            android:layout_height="10dp"
+            android:layout_width="match_parent"/>
+
+    </LinearLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/quick_settings_header.xml b/packages/SystemUI/res/layout/quick_settings_header.xml
new file mode 100644
index 0000000..43197c4
--- /dev/null
+++ b/packages/SystemUI/res/layout/quick_settings_header.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 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
+  -->
+<com.android.systemui.qs.QSTooltipView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/qs_header_tooltip_height"
+    android:alpha="0"
+    android:gravity="center_horizontal|bottom"
+    android:visibility="invisible">
+
+    <TextView
+        android:id="@+id/header_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/quick_settings_header_onboarding_text"
+        android:textAppearance="@style/TextAppearance.QS.TileLabel"
+        android:textColor="?android:attr/colorAccent" />
+
+</com.android.systemui.qs.QSTooltipView>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index a5e37d5..13ca114 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -40,7 +40,7 @@
     <dimen name="battery_detail_graph_space_top">27dp</dimen>
     <dimen name="battery_detail_graph_space_bottom">27dp</dimen>
 
-    <dimen name="qs_tile_margin_top">16dp</dimen>
+    <dimen name="qs_tile_margin_top">32dp</dimen>
     <dimen name="qs_brightness_padding_top">6dp</dimen>
     <dimen name="qs_detail_margin_top">28dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/attrs_car.xml b/packages/SystemUI/res/values/attrs_car.xml
new file mode 100644
index 0000000..b1097c3
--- /dev/null
+++ b/packages/SystemUI/res/values/attrs_car.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<resources>
+    <!-- Allow for custom attribs to be added to a facet button -->
+    <declare-styleable name="CarFacetButton">
+        <!-- icon to be rendered (drawable) -->
+        <attr name="icon" format="reference"/>
+        <!-- intent to start when button is click -->
+        <attr name="intent" format="string"/>
+        <!-- intent to start when a long press has happened -->
+        <attr name="longIntent" format="string"/>
+        <!-- categories that will be added as extras to the fired intents -->
+        <attr name="categories" format="string"/>
+        <!-- package names that will be added as extras to the fired intents -->
+        <attr name="packages" format="string" />
+    </declare-styleable>
+
+
+    <!-- Allow for custom attribs to be added to a nav button -->
+    <declare-styleable name="CarNavigationButton">
+        <!-- intent to start when button is click -->
+        <attr name="intent" format="string"/>
+        <!-- intent to start when a long press has happened -->
+        <attr name="longIntent" format="string"/>
+        <!-- start the intent as a broad cast instead of an activity if true-->
+        <attr name="broadcast" format="boolean"/>
+    </declare-styleable>
+</resources>
diff --git a/packages/SystemUI/res/values/config_car.xml b/packages/SystemUI/res/values/config_car.xml
index 9c8dcb1..db829f2 100644
--- a/packages/SystemUI/res/values/config_car.xml
+++ b/packages/SystemUI/res/values/config_car.xml
@@ -22,4 +22,9 @@
          uri that will be launched into the docked window. -->
     <bool name="config_enablePersistentDockedActivity">false</bool>
     <string name="config_persistentDockedActivityIntentUri" translatable="false"></string>
+
+    <!-- configure which system ui bars should be displayed -->
+    <bool name="config_enableLeftNavigationBar">false</bool>
+    <bool name="config_enableRightNavigationBar">false</bool>
+    <bool name="config_enableBottomNavigationBar">true</bool>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d11ab42..bc828ff 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -290,7 +290,7 @@
 
     <dimen name="qs_tile_height">106dp</dimen>
     <dimen name="qs_tile_margin">19dp</dimen>
-    <dimen name="qs_tile_margin_top">16dp</dimen>
+    <dimen name="qs_tile_margin_top">32dp</dimen>
     <dimen name="qs_quick_tile_size">48dp</dimen>
     <dimen name="qs_quick_tile_padding">12dp</dimen>
     <dimen name="qs_header_gear_translation">16dp</dimen>
@@ -309,6 +309,7 @@
     <dimen name="qs_tile_padding_bottom">16dp</dimen>
     <dimen name="qs_tile_spacing">4dp</dimen>
     <dimen name="qs_panel_padding_bottom">0dp</dimen>
+    <dimen name="qs_panel_padding_top">32dp</dimen>
     <dimen name="qs_detail_header_height">56dp</dimen>
     <dimen name="qs_detail_header_padding">0dp</dimen>
     <dimen name="qs_detail_image_width">56dp</dimen>
@@ -333,6 +334,9 @@
     <dimen name="qs_detail_item_icon_width">32dp</dimen>
     <dimen name="qs_detail_item_icon_marginStart">0dp</dimen>
     <dimen name="qs_detail_item_icon_marginEnd">20dp</dimen>
+    <dimen name="qs_header_padding_start">16dp</dimen>
+    <dimen name="qs_header_padding_end">24dp</dimen>
+    <dimen name="qs_header_tooltip_height">32dp</dimen>
     <dimen name="qs_footer_padding_start">16dp</dimen>
     <dimen name="qs_footer_padding_end">24dp</dimen>
     <dimen name="qs_footer_icon_size">16dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8c59e75..86cab22 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -666,6 +666,8 @@
     <!-- Textual description of Ethernet connections -->
     <string name="ethernet_label">Ethernet</string>
 
+    <!-- QuickSettings: Onboarding text that introduces users to long press on an option in order to view the option's menu in Settings [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_header_onboarding_text">Press &amp; hold on the icons for more options</string>
     <!-- QuickSettings: Do not disturb [CHAR LIMIT=NONE] -->
     <string name="quick_settings_dnd_label">Do not disturb</string>
     <!-- QuickSettings: Do not disturb - Priority only [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 4cf817e..b8319a8e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -44,4 +44,9 @@
      * Specifies the text to be shown for onboarding the new swipe-up gesture to access recents.
      */
     void setRecentsOnboardingText(CharSequence text);
+
+    /**
+     * Enables/disables launcher/overview interaction features {@link InteractionType}.
+     */
+    void setInteractionState(int flags);
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
index f622d4a..17191868 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -28,4 +28,33 @@
     public static final int HIT_TARGET_NONE = 0;
     public static final int HIT_TARGET_BACK = 1;
     public static final int HIT_TARGET_HOME = 2;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({FLAG_DISABLE_SWIPE_UP,
+            FLAG_DISABLE_QUICK_SCRUB,
+            FLAG_SHOW_OVERVIEW_BUTTON,
+            FLAG_HIDE_BACK_BUTTON
+    })
+    public @interface InteractionType {}
+
+    /**
+     * Interaction type: whether the gesture to swipe up from the navigation bar will trigger
+     * launcher to show overview
+     */
+
+    public static final int FLAG_DISABLE_SWIPE_UP = 0x1;
+    /**
+     * Interaction type: enable quick scrub and switch interaction on the home button
+     */
+    public static final int FLAG_DISABLE_QUICK_SCRUB = 0x2;
+
+    /**
+     * Interaction type: show/hide the overview button while this service is connected to launcher
+     */
+    public static final int FLAG_SHOW_OVERVIEW_BUTTON = 0x4;
+
+    /**
+     * Interaction type: show/hide the back button while this service is connected to launcher
+     */
+    public static final int FLAG_HIDE_BACK_BUTTON = 0x8;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index d0128ef..1185f45 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -47,6 +47,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
+
 /**
  * Class to send information from overview to launcher with a binder.
  */
@@ -67,6 +69,7 @@
     private IOverviewProxy mOverviewProxy;
     private int mConnectionBackoffAttempts;
     private CharSequence mOnboardingText;
+    private @InteractionType int mInteractionFlags;
 
     private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
 
@@ -108,6 +111,22 @@
         public void setRecentsOnboardingText(CharSequence text) {
             mOnboardingText = text;
         }
+
+        public void setInteractionState(@InteractionType int flags) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                if (mInteractionFlags != flags) {
+                    mInteractionFlags = flags;
+                    mHandler.post(() -> {
+                        for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+                            mConnectionCallbacks.get(i).onInteractionFlagsChanged(flags);
+                        }
+                    });
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     };
 
     private final BroadcastReceiver mLauncherAddedReceiver = new BroadcastReceiver() {
@@ -230,6 +249,10 @@
         return mOnboardingText;
     }
 
+    public int getInteractionFlags() {
+        return mInteractionFlags;
+    }
+
     private void disconnectFromLauncherService() {
         if (mOverviewProxy != null) {
             mOverviewProxy.asBinder().unlinkToDeath(mOverviewServiceDeathRcpt, 0);
@@ -263,5 +286,6 @@
     public interface OverviewProxyListener {
         default void onConnectionChanged(boolean isConnected) {}
         default void onRecentsAnimationStarted() {}
+        default void onInteractionFlagsChanged(@InteractionType int flags) {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index adb4e33..8b57740 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -47,6 +47,7 @@
         Key.QS_INVERT_COLORS_ADDED,
         Key.QS_WORK_ADDED,
         Key.QS_NIGHTDISPLAY_ADDED,
+        Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
         Key.SEEN_MULTI_USER,
         Key.NUM_APPS_LAUNCHED,
         Key.HAS_SEEN_RECENTS_ONBOARDING,
@@ -76,6 +77,11 @@
         String QS_WORK_ADDED = "QsWorkAdded";
         @Deprecated
         String QS_NIGHTDISPLAY_ADDED = "QsNightDisplayAdded";
+        /**
+         * Used for tracking how many times the user has seen the long press tooltip in the Quick
+         * Settings panel.
+         */
+        String QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT = "QsLongPressTooltipShownCount";
         String SEEN_MULTI_USER = "HasSeenMultiUser";
         String NUM_APPS_LAUNCHED = "NumAppsLaunched";
         String HAS_SEEN_RECENTS_ONBOARDING = "HasSeenRecentsOnboarding";
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 222c6e82..fccd9ce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -44,6 +44,7 @@
 
     public static final float EXPANDED_TILE_DELAY = .86f;
 
+
     private final ArrayList<View> mAllViews = new ArrayList<>();
     /**
      * List of {@link View}s representing Quick Settings that are being animated from the quick QS
@@ -65,6 +66,11 @@
     private TouchAnimator mNonfirstPageDelayedAnimator;
     private TouchAnimator mBrightnessAnimator;
 
+    /**
+     * Whether the animation is stable and not in the middle of animating between the collapsed and
+     * expanded states.
+     */
+    private boolean mIsInStableState;
     private boolean mOnKeyguard;
 
     private boolean mAllowFancy;
@@ -89,6 +95,10 @@
             Log.w(TAG, "QS Not using page layout");
         }
         panel.setPageListener(this);
+
+        // At time of creation, the QS panel is always considered stable as it's not in the middle
+        // of collapse/expanded.
+        mIsInStableState = true;
     }
 
     public void onRtlChanged() {
@@ -243,6 +253,11 @@
             } else {
                 mBrightnessAnimator = null;
             }
+            View headerView = mQsPanel.getHeaderView();
+            if (headerView!= null) {
+                firstPageBuilder.addFloat(headerView, "translationY", heightDiff, 0);
+                mAllViews.add(headerView);
+            }
             mFirstPageAnimator = firstPageBuilder
                     .setListener(this)
                     .build();
@@ -326,11 +341,21 @@
 
     @Override
     public void onAnimationAtStart() {
+        if (!mIsInStableState) {
+            mQsPanel.onCollapse();
+        }
+        mIsInStableState = true;
+
         mQuickQsPanel.setVisibility(View.VISIBLE);
     }
 
     @Override
     public void onAnimationAtEnd() {
+        if (!mIsInStableState) {
+            mQsPanel.onExpanded();
+        }
+        mIsInStableState = true;
+
         mQuickQsPanel.setVisibility(View.INVISIBLE);
         final int N = mQuickQsViews.size();
         for (int i = 0; i < N; i++) {
@@ -340,6 +365,11 @@
 
     @Override
     public void onAnimationStarted() {
+        if (mIsInStableState) {
+            mQsPanel.onAnimating();
+        }
+        mIsInStableState = false;
+
         mQuickQsPanel.setVisibility(mOnKeyguard ? View.INVISIBLE : View.VISIBLE);
         if (mOnFirstPage) {
             final int N = mQuickQsViews.size();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index f7c388d..5640be5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState;
 
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -58,6 +59,7 @@
 public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener {
 
     public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness";
+    public static final String QS_SHOW_LONG_PRESS_TOOLTIP = "qs_show_long_press";
 
     protected final Context mContext;
     protected final ArrayList<TileRecord> mRecords = new ArrayList<TileRecord>();
@@ -72,6 +74,7 @@
     private BrightnessController mBrightnessController;
     protected QSTileHost mHost;
 
+    protected QSTooltipView mTooltipView;
     protected QSSecurityFooter mFooter;
     private boolean mGridContentVisible = true;
 
@@ -94,6 +97,9 @@
 
         setOrientation(VERTICAL);
 
+        mTooltipView = (QSTooltipView) LayoutInflater.from(mContext)
+                .inflate(R.layout.quick_settings_header, this, false);
+
         mBrightnessView = LayoutInflater.from(mContext).inflate(
             R.layout.quick_settings_brightness_dialog, this, false);
         mTileLayout = new TileLayout(mContext);
@@ -101,7 +107,12 @@
         Space space = new Space(mContext);
         space.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                 mContext.getResources().getDimensionPixelSize(R.dimen.qs_footer_height)));
-        mScrollLayout = new QSScrollLayout(mContext, mBrightnessView, (View) mTileLayout, space);
+        mScrollLayout = new QSScrollLayout(
+                mContext,
+                mTooltipView,
+                mBrightnessView,
+                (View) mTileLayout,
+                space);
         addView(mScrollLayout);
 
         addDivider();
@@ -134,7 +145,10 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        Dependency.get(TunerService.class).addTunable(this, QS_SHOW_BRIGHTNESS);
+        final TunerService tunerService = Dependency.get(TunerService.class);
+        tunerService.addTunable(this, QS_SHOW_BRIGHTNESS);
+        tunerService.addTunable(this, QS_SHOW_LONG_PRESS_TOOLTIP);
+
         if (mHost != null) {
             setTiles(mHost.getTiles());
         }
@@ -166,11 +180,16 @@
     @Override
     public void onTuningChanged(String key, String newValue) {
         if (QS_SHOW_BRIGHTNESS.equals(key)) {
-            mBrightnessView.setVisibility(newValue == null || Integer.parseInt(newValue) != 0
-                    ? VISIBLE : GONE);
+            updateViewVisibilityForTuningValue(mBrightnessView, newValue);
+        } else if (QS_SHOW_LONG_PRESS_TOOLTIP.equals(key)) {
+            updateViewVisibilityForTuningValue(mTooltipView, newValue);
         }
     }
 
+    private void updateViewVisibilityForTuningValue(View view, @Nullable String newValue) {
+        view.setVisibility(newValue == null || Integer.parseInt(newValue) != 0 ? VISIBLE : GONE);
+    }
+
     public void openDetails(String subPanel) {
         QSTile tile = getTile(subPanel);
         showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0});
@@ -205,6 +224,10 @@
         return mBrightnessView;
     }
 
+    View getHeaderView() {
+        return mTooltipView;
+    }
+
     public void setCallback(QSDetail.Callback callback) {
         mCallback = callback;
     }
@@ -266,11 +289,27 @@
         if (mCustomizePanel != null && mCustomizePanel.isShown()) {
             mCustomizePanel.hide(mCustomizePanel.getWidth() / 2, mCustomizePanel.getHeight() / 2);
         }
+
+        // Instantly hide the header here since we don't want it to still be animating.
+        mTooltipView.setVisibility(View.INVISIBLE);
+    }
+
+    /**
+     * Called when the panel is fully animated out/expanded. This is different from the state
+     * tracked by {@link #mExpanded}, which only checks if the panel is even partially pulled out.
+     */
+    public void onExpanded() {
+        mTooltipView.fadeIn();
+    }
+
+    public void onAnimating() {
+        mTooltipView.fadeOut();
     }
 
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
         mExpanded = expanded;
+
         if (!mExpanded) {
             if (mTileLayout instanceof PagedTileLayout) {
                 ((PagedTileLayout) mTileLayout).setCurrentItem(0, false);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java
new file mode 100644
index 0000000..d1f9741
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTooltipView.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 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 com.android.systemui.qs;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.systemui.Prefs;
+
+import java.util.concurrent.TimeUnit;
+
+
+/**
+ * Tooltip/header view for the Quick Settings panel.
+ */
+public class QSTooltipView extends LinearLayout {
+
+    private static final int FADE_ANIMATION_DURATION_MS = 300;
+    private static final long AUTO_FADE_OUT_DELAY_MS = TimeUnit.SECONDS.toMillis(6);
+    private static final int TOOLTIP_NOT_YET_SHOWN_COUNT = 0;
+    public static final int MAX_TOOLTIP_SHOWN_COUNT = 3;
+
+    private final Handler mHandler = new Handler();
+    private final Runnable mAutoFadeOutRunnable = () -> fadeOut();
+
+    private int mShownCount;
+
+    public QSTooltipView(Context context) {
+        this(context, null);
+    }
+
+    public QSTooltipView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mShownCount = getStoredShownCount();
+    }
+
+    /** Returns the latest stored tooltip shown count from SharedPreferences. */
+    private int getStoredShownCount() {
+        return Prefs.getInt(
+                mContext,
+                Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
+                TOOLTIP_NOT_YET_SHOWN_COUNT);
+    }
+
+    /**
+     * Fades in the header view if we can show the tooltip - short circuits any running animation.
+     */
+    public void fadeIn() {
+        if (mShownCount < MAX_TOOLTIP_SHOWN_COUNT) {
+            animate().cancel();
+            setVisibility(View.VISIBLE);
+            animate()
+                    .alpha(1f)
+                    .setDuration(FADE_ANIMATION_DURATION_MS)
+                    .setListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            mHandler.postDelayed(mAutoFadeOutRunnable, AUTO_FADE_OUT_DELAY_MS);
+                        }
+                    })
+                    .start();
+
+            // Increment and drop the shown count in prefs for the next time we're deciding to
+            // fade in the tooltip. We first sanity check that the tooltip count hasn't changed yet
+            // in prefs (say, from a long press).
+            if (getStoredShownCount() <= mShownCount) {
+                Prefs.putInt(mContext, Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, ++mShownCount);
+            }
+        }
+    }
+
+    /**
+     * Fades out the header view if it's partially visible - short circuits any running animation.
+     */
+    public void fadeOut() {
+        animate().cancel();
+        if (getVisibility() == View.VISIBLE && getAlpha() != 0f) {
+            mHandler.removeCallbacks(mAutoFadeOutRunnable);
+            animate()
+                    .alpha(0f)
+                    .setDuration(FADE_ANIMATION_DURATION_MS)
+                    .setListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            perhapsMakeViewInvisible();
+                        }
+                    })
+                    .start();
+        } else {
+            perhapsMakeViewInvisible();
+        }
+    }
+
+    /**
+     * Only update visibility if the view is currently being shown. Otherwise, it's already been
+     * hidden by some other manner.
+     */
+    private void perhapsMakeViewInvisible() {
+        if (getVisibility() == View.VISIBLE) {
+            setVisibility(View.INVISIBLE);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 8314855..1b4b7df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -124,9 +124,8 @@
 
     @Override
     public void onTuningChanged(String key, String newValue) {
-        // No tunings for you.
-        if (key.equals(QS_SHOW_BRIGHTNESS)) {
-            // No Brightness for you.
+        if (QS_SHOW_BRIGHTNESS.equals(key) || QS_SHOW_LONG_PRESS_TOOLTIP.equals(key)) {
+            // No Brightness or Tooltip for you!
             super.onTuningChanged(key, "0");
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 65135ab..9fa7beb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -99,7 +99,11 @@
             record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight));
             previousView = record.tileView.updateAccessibilityOrder(previousView);
         }
-        int height = (mCellHeight + mCellMargin) * rows + (mCellMarginTop - mCellMargin);
+
+        // Only include the top margin in our measurement if we have more than 1 row to show.
+        // Otherwise, don't add the extra margin buffer at top.
+        int height = (mCellHeight + mCellMargin) * rows +
+                rows != 0 ? (mCellMarginTop - mCellMargin) : 0;
         if (height < 0) height = 0;
         setMeasuredDimension(width, height);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
index 37f2528..6263efa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
@@ -107,7 +107,7 @@
         void onAnimationAtStart();
 
         /**
-         * Called when the animator moves into a position of "0". Start and end delays are
+         * Called when the animator moves into a position of "1". Start and end delays are
          * taken into account, so this position may cover a range of fractional inputs.
          */
         void onAnimationAtEnd();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 72592829..016cbd6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -42,6 +42,7 @@
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
+import com.android.systemui.Prefs;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.plugins.qs.QSIconView;
@@ -49,6 +50,7 @@
 import com.android.systemui.plugins.qs.QSTile.State;
 import com.android.systemui.qs.PagedTileLayout.TilePage;
 import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.QSTooltipView;
 
 import java.util.ArrayList;
 
@@ -191,6 +193,11 @@
     public void longClick() {
         mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)));
         mHandler.sendEmptyMessage(H.LONG_CLICK);
+
+        Prefs.putInt(
+                mContext,
+                Prefs.Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT,
+                QSTooltipView.MAX_TOOLTIP_SHOWN_COUNT);
     }
 
     public LogMaker populate(LogMaker logMaker) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
new file mode 100644
index 0000000..53101a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
@@ -0,0 +1,161 @@
+package com.android.systemui.statusbar.car;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import com.android.keyguard.AlphaOptimizedImageButton;
+import com.android.systemui.R;
+
+/**
+ * CarFacetButton is a ui component designed to be used as a shortcut for an app of a defined
+ * category. It can also render a indicator impling that there are more options of apps to launch
+ * using this component. This is done with a "More icon" currently an arrow as defined in the layout
+ * file. The class is to serve as an example.
+ * Usage example: A button that allows a user to select a music app and indicate that there are
+ * other music apps installed.
+ */
+public class CarFacetButton extends LinearLayout {
+    private static final float SELECTED_ALPHA = 1f;
+    private static final float UNSELECTED_ALPHA = 0.7f;
+
+    private static final String FACET_FILTER_DELIMITER = ";";
+    /**
+     * Extra information to be sent to a helper to make the decision of what app to launch when
+     * clicked.
+     */
+    private static final String EXTRA_FACET_CATEGORIES = "categories";
+    private static final String EXTRA_FACET_PACKAGES = "packages";
+    private static final String EXTRA_FACET_ID = "filter_id";
+    private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
+
+    private Context mContext;
+    private AlphaOptimizedImageButton mIcon;
+    private AlphaOptimizedImageButton mMoreIcon;
+    private boolean mSelected = false;
+    /** App categories that are to be used with this widget */
+    private String[] mFacetCategories;
+    /** App packages that are allowed to be used with this widget */
+    private String[] mFacetPackages;
+
+
+    public CarFacetButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        View.inflate(context, R.layout.car_facet_button, this);
+
+        // extract custom attributes
+        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
+        setupIntents(typedArray);
+        setupIcons(typedArray);
+    }
+
+    /**
+     * Reads the custom attributes to setup click handlers for this component.
+     */
+    private void setupIntents(TypedArray typedArray) {
+        String intentString = typedArray.getString(R.styleable.CarFacetButton_intent);
+        String longPressIntentString = typedArray.getString(R.styleable.CarFacetButton_longIntent);
+        String categoryString = typedArray.getString(R.styleable.CarFacetButton_categories);
+        String packageString = typedArray.getString(R.styleable.CarFacetButton_packages);
+        try {
+            final Intent intent = Intent.parseUri(intentString, Intent.URI_INTENT_SCHEME);
+            intent.putExtra(EXTRA_FACET_ID, Integer.toString(getId()));
+
+            if (packageString != null) {
+                mFacetPackages = packageString.split(FACET_FILTER_DELIMITER);
+                intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages);
+            }
+            if (categoryString != null) {
+                mFacetCategories = categoryString.split(FACET_FILTER_DELIMITER);
+                intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories);
+            }
+
+            setOnClickListener(v -> {
+                intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, mSelected);
+                mContext.startActivity(intent);
+            });
+
+            if (longPressIntentString != null) {
+                final Intent longPressIntent = Intent.parseUri(longPressIntentString,
+                        Intent.URI_INTENT_SCHEME);
+                setOnLongClickListener(v -> {
+                    mContext.startActivity(longPressIntent);
+                    return true;
+                });
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to attach intent", e);
+        }
+    }
+
+
+    private void setupIcons(TypedArray styledAttributes) {
+        mIcon = findViewById(R.id.car_nav_button_icon);
+        mIcon.setScaleType(ImageView.ScaleType.CENTER);
+        mIcon.setClickable(false);
+        mIcon.setAlpha(UNSELECTED_ALPHA);
+        int iconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
+        if (iconResourceId == 0)  {
+            throw new RuntimeException("specified icon resource was not found and is required");
+        }
+        mIcon.setImageResource(iconResourceId);
+
+        mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
+        mMoreIcon.setClickable(false);
+        mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow));
+        mMoreIcon.setAlpha(UNSELECTED_ALPHA);
+        mMoreIcon.setVisibility(GONE);
+    }
+
+    /**
+     * @return The app categories the component represents
+     */
+    public String[] getCategories() {
+        if (mFacetCategories == null) {
+            return new String[0];
+        }
+        return mFacetCategories;
+    }
+
+    /**
+     * @return The valid packages that should be considered.
+     */
+    public String[] getFacetPackages() {
+        if (mFacetPackages == null) {
+            return new String[0];
+        }
+        return mFacetPackages;
+    }
+
+    /**
+     * Updates the alpha of the icons to "selected" and shows the "More icon"
+     * @param selected true if the view must be selected, false otherwise
+     */
+    public void setSelected(boolean selected) {
+        super.setSelected(selected);
+        setSelected(selected, selected);
+    }
+
+    /**
+     * Updates the visual state to let the user know if it's been selected.
+     * @param selected true if should update the alpha of the icon to selected, false otherwise
+     * @param showMoreIcon true if the "more icon" should be shown, false otherwise
+     */
+    public void setSelected(boolean selected, boolean showMoreIcon) {
+        mSelected = selected;
+        if (selected) {
+            mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
+            mMoreIcon.setAlpha(SELECTED_ALPHA);
+            mIcon.setAlpha(SELECTED_ALPHA);
+        } else {
+            mMoreIcon.setVisibility(GONE);
+            mIcon.setAlpha(UNSELECTED_ALPHA);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
new file mode 100644
index 0000000..e8c9a5e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -0,0 +1,114 @@
+package com.android.systemui.statusbar.car;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * CarFacetButtons placed on the nav bar are designed to have visual indication that the active
+ * application on screen is associated with it. This is basically a similar concept to a radio
+ * button group.
+ */
+public class CarFacetButtonController {
+
+    protected HashMap<String, CarFacetButton> mButtonsByCategory = new HashMap<>();
+    protected HashMap<String, CarFacetButton> mButtonsByPackage = new HashMap<>();
+    protected CarFacetButton mSelectedFacetButton;
+    protected Context mContext;
+
+    public CarFacetButtonController(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Goes through the supplied CarNavigationBarView and keeps track of all the CarFacetButtons
+     * such that it can select and unselect them based on running task chages
+     * @param bar that may contain CarFacetButtons
+     */
+    public void addCarNavigationBar(CarNavigationBarView bar) {
+        findFacets(bar);
+    }
+
+    private void findFacets(ViewGroup root) {
+        final int childCount = root.getChildCount();
+
+        for (int i = 0; i < childCount; ++i) {
+            final View v = root.getChildAt(i);
+            if (v instanceof CarFacetButton) {
+                CarFacetButton facetButton = (CarFacetButton) v;
+                String[] categories = facetButton.getCategories();
+                for (int j = 0; j < categories.length; j++) {
+                    String category = categories[j];
+                    mButtonsByCategory.put(category, facetButton);
+                }
+
+                String[] facetPackages = facetButton.getFacetPackages();
+                for (int j = 0; j < facetPackages.length; j++) {
+                    String facetPackage = facetPackages[j];
+                    mButtonsByPackage.put(facetPackage, facetButton);
+                }
+            } else if (v instanceof ViewGroup) {
+                findFacets((ViewGroup) v);
+            }
+        }
+    }
+
+
+    /**
+     * This will unselect the currently selected CarFacetButton and determine which one should be
+     * selected next. It does this by reading the properties on the CarFacetButton and seeing if
+     * they are a match with the supplied taskino.
+     * @param taskInfo of the currently running application
+     */
+    public void taskChanged(ActivityManager.RunningTaskInfo taskInfo) {
+        if (taskInfo == null || taskInfo.baseActivity == null) {
+            return;
+        }
+        String packageName = taskInfo.baseActivity.getPackageName();
+
+        // If the package name belongs to a filter, then highlight appropriate button in
+        // the navigation bar.
+        if (mSelectedFacetButton != null) {
+            mSelectedFacetButton.setSelected(false);
+        }
+        CarFacetButton facetButton = mButtonsByPackage.get(packageName);
+        if (facetButton != null) {
+            facetButton.setSelected(true);
+            mSelectedFacetButton = facetButton;
+        } else {
+            String category = getPackageCategory(packageName);
+            if (category != null) {
+                facetButton = mButtonsByCategory.get(category);
+                facetButton.setSelected(true);
+                mSelectedFacetButton = facetButton;
+            }
+        }
+    }
+
+    protected String getPackageCategory(String packageName) {
+        PackageManager pm = mContext.getPackageManager();
+        Set<String> supportedCategories = mButtonsByCategory.keySet();
+        for (String category : supportedCategories) {
+            Intent intent = new Intent();
+            intent.setPackage(packageName);
+            intent.setAction(Intent.ACTION_MAIN);
+            intent.addCategory(category);
+            List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
+            if (list.size() > 0) {
+                // Cache this package name into facetPackageMap, so we won't have to query
+                // all categories next time this package name shows up.
+                mButtonsByPackage.put(packageName, mButtonsByCategory.get(category));
+                return category;
+            }
+        }
+        return null;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
deleted file mode 100644
index 64c52ed..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
+++ /dev/null
@@ -1,407 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.statusbar.car;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.support.v4.util.SimpleArrayMap;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.SparseBooleanArray;
-import android.view.View;
-import android.widget.LinearLayout;
-import com.android.systemui.R;
-
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A controller to populate data for CarNavigationBarView and handle user interactions.
- *
- * <p>Each button inside the navigation bar is defined by data in arrays_car.xml. OEMs can
- * customize the navigation buttons by updating arrays_car.xml appropriately in an overlay.
- */
-class CarNavigationBarController {
-    private static final String TAG = "CarNavBarController";
-
-    private static final String EXTRA_FACET_CATEGORIES = "categories";
-    private static final String EXTRA_FACET_PACKAGES = "packages";
-    private static final String EXTRA_FACET_ID = "filter_id";
-    private static final String EXTRA_FACET_LAUNCH_PICKER = "launch_picker";
-
-    /**
-     * Each facet of the navigation bar maps to a set of package names or categories defined in
-     * arrays_car.xml. Package names for a given facet are delimited by ";".
-     */
-    private static final String FACET_FILTER_DELIMITER = ";";
-
-    private final Context mContext;
-    private final CarNavigationBarView mNavBar;
-    private final CarStatusBar mStatusBar;
-
-    /**
-     * Set of categories each facet will filter on.
-     */
-    private final List<String[]> mFacetCategories = new ArrayList<>();
-
-    /**
-     * Set of package names each facet will filter on.
-     */
-    private final List<String[]> mFacetPackages = new ArrayList<>();
-
-    private final SimpleArrayMap<String, Integer> mFacetCategoryMap = new SimpleArrayMap<>();
-    private final SimpleArrayMap<String, Integer> mFacetPackageMap = new SimpleArrayMap<>();
-
-    private final List<CarNavigationButton> mNavButtons = new ArrayList<>();
-
-    private final SparseBooleanArray mFacetHasMultipleAppsCache = new SparseBooleanArray();
-
-    private int mCurrentFacetIndex;
-    private Intent mPersistentTaskIntent;
-
-    public CarNavigationBarController(Context context, CarNavigationBarView navBar,
-            CarStatusBar activityStarter) {
-        mContext = context;
-        mNavBar = navBar;
-        mStatusBar = activityStarter;
-        bind();
-
-        if (context.getResources().getBoolean(R.bool.config_enablePersistentDockedActivity)) {
-            setupPersistentDockedTask();
-        }
-    }
-
-    private void setupPersistentDockedTask() {
-        try {
-            mPersistentTaskIntent = Intent.parseUri(
-                    mContext.getString(R.string.config_persistentDockedActivityIntentUri),
-                    Intent.URI_INTENT_SCHEME);
-        } catch (URISyntaxException e) {
-            Log.e(TAG, "Malformed persistent task intent.");
-        }
-    }
-
-    public void taskChanged(String packageName, ActivityManager.RunningTaskInfo taskInfo) {
-        // If the package name belongs to a filter, then highlight appropriate button in
-        // the navigation bar.
-        if (mFacetPackageMap.containsKey(packageName)) {
-            setCurrentFacet(mFacetPackageMap.get(packageName));
-        }
-
-        // Check if the package matches any of the categories for the facets
-        String category = getPackageCategory(packageName);
-        if (category != null) {
-            setCurrentFacet(mFacetCategoryMap.get(category));
-        }
-
-        // Set up the persistent docked task if needed.
-        boolean isHomeTask =
-                taskInfo.configuration.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME;
-        if (mPersistentTaskIntent != null && !mStatusBar.hasDockedTask() && !isHomeTask) {
-            mStatusBar.startActivityOnStack(mPersistentTaskIntent,
-                    WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
-        }
-    }
-
-    public void onPackageChange(String packageName) {
-        if (mFacetPackageMap.containsKey(packageName)) {
-            int index = mFacetPackageMap.get(packageName);
-            mFacetHasMultipleAppsCache.put(index, facetHasMultiplePackages(index));
-            // No need to check categories because we've already refreshed the cache.
-            return;
-        }
-
-        String category = getPackageCategory(packageName);
-        if (mFacetCategoryMap.containsKey(category)) {
-            int index = mFacetCategoryMap.get(category);
-            mFacetHasMultipleAppsCache.put(index, facetHasMultiplePackages(index));
-        }
-    }
-
-    /**
-     * Iterates through the items in arrays_car.xml and sets up the facet bar buttons to
-     * perform the task in that configuration file when clicked or long-pressed.
-     */
-    private void bind() {
-        Resources res = mContext.getResources();
-
-        TypedArray icons = res.obtainTypedArray(R.array.car_facet_icons);
-        TypedArray intents = res.obtainTypedArray(R.array.car_facet_intent_uris);
-        TypedArray longPressIntents = res.obtainTypedArray(R.array.car_facet_longpress_intent_uris);
-        TypedArray facetPackageNames = res.obtainTypedArray(R.array.car_facet_package_filters);
-        TypedArray facetCategories = res.obtainTypedArray(R.array.car_facet_category_filters);
-
-        try {
-            if (icons.length() != intents.length()
-                    || icons.length() != longPressIntents.length()
-                    || icons.length() != facetPackageNames.length()
-                    || icons.length() != facetCategories.length()) {
-                throw new RuntimeException("car_facet array lengths do not match");
-            }
-
-            for (int i = 0, size = icons.length(); i < size; i++) {
-                Drawable icon = icons.getDrawable(i);
-                CarNavigationButton button = createNavButton(icon);
-                initClickListeners(button, i, intents.getString(i), longPressIntents.getString(i));
-
-                mNavButtons.add(button);
-                mNavBar.addButton(button, createNavButton(icon) /* lightsOutButton */);
-
-                initFacetFilterMaps(i, facetPackageNames.getString(i).split(FACET_FILTER_DELIMITER),
-                        facetCategories.getString(i).split(FACET_FILTER_DELIMITER));
-                mFacetHasMultipleAppsCache.put(i, facetHasMultiplePackages(i));
-            }
-        } finally {
-            // Clean up all the TypedArrays.
-            icons.recycle();
-            intents.recycle();
-            longPressIntents.recycle();
-            facetPackageNames.recycle();
-            facetCategories.recycle();
-        }
-    }
-
-    /**
-     * Recreates each of the buttons on a density or font scale change. This manual process is
-     * necessary since this class is not part of an activity that automatically gets recreated.
-     */
-    public void onDensityOrFontScaleChanged() {
-        TypedArray icons = mContext.getResources().obtainTypedArray(R.array.car_facet_icons);
-
-        try {
-            int length = icons.length();
-            if (length != mNavButtons.size()) {
-                // This should not happen since the mNavButtons list is created from the length
-                // of the icons array in bind().
-                throw new RuntimeException("car_facet array lengths do not match number of "
-                        + "created buttons.");
-            }
-
-            for (int i = 0; i < length; i++) {
-                Drawable icon = icons.getDrawable(i);
-
-                // Setting a new icon will trigger a requestLayout() call if necessary.
-                mNavButtons.get(i).setResources(icon);
-            }
-        } finally {
-            icons.recycle();
-        }
-    }
-
-    private void initFacetFilterMaps(int id, String[] packageNames, String[] categories) {
-        mFacetCategories.add(categories);
-        for (String category : categories) {
-            mFacetCategoryMap.put(category, id);
-        }
-
-        mFacetPackages.add(packageNames);
-        for (String packageName : packageNames) {
-            mFacetPackageMap.put(packageName, id);
-        }
-    }
-
-    private String getPackageCategory(String packageName) {
-        PackageManager pm = mContext.getPackageManager();
-        int size = mFacetCategories.size();
-        // For each facet, check if the given package name matches one of its categories
-        for (int i = 0; i < size; i++) {
-            String[] categories = mFacetCategories.get(i);
-            for (int j = 0; j < categories.length; j++) {
-                String category = categories[j];
-                Intent intent = new Intent();
-                intent.setPackage(packageName);
-                intent.setAction(Intent.ACTION_MAIN);
-                intent.addCategory(category);
-                List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
-                if (list.size() > 0) {
-                    // Cache this package name into facetPackageMap, so we won't have to query
-                    // all categories next time this package name shows up.
-                    mFacetPackageMap.put(packageName, mFacetCategoryMap.get(category));
-                    return category;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Helper method to check if a given facet has multiple packages associated with it. This can
-     * be resource defined package names or package names filtered by facet category.
-     *
-     * @return {@code true} if the facet at the given index has more than one package.
-     */
-    private boolean facetHasMultiplePackages(int index) {
-        PackageManager pm = mContext.getPackageManager();
-
-        // Check if the packages defined for the filter actually exists on the device
-        String[] packages = mFacetPackages.get(index);
-        if (packages.length > 1) {
-            int count = 0;
-            for (int i = 0; i < packages.length; i++) {
-                count += pm.getLaunchIntentForPackage(packages[i]) != null ? 1 : 0;
-                if (count > 1) {
-                    return true;
-                }
-            }
-        }
-
-        // If there weren't multiple packages defined for the facet, check the categories
-        // and see if they resolve to multiple package names
-        String categories[] = mFacetCategories.get(index);
-
-        int count = 0;
-        for (int i = 0; i < categories.length; i++) {
-            String category = categories[i];
-            Intent intent = new Intent();
-            intent.setAction(Intent.ACTION_MAIN);
-            intent.addCategory(category);
-            count += pm.queryIntentActivities(intent, 0).size();
-            if (count > 1) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Sets the facet at the given index to be the facet that is currently active. The button will
-     * be highlighted appropriately.
-     */
-    private void setCurrentFacet(int index) {
-        if (index == mCurrentFacetIndex) {
-            return;
-        }
-
-        if (mNavButtons.get(mCurrentFacetIndex) != null) {
-            mNavButtons.get(mCurrentFacetIndex)
-                    .setSelected(false /* selected */, false /* showMoreIcon */);
-        }
-
-        if (mNavButtons.get(index) != null) {
-            mNavButtons.get(index).setSelected(true /* selected */,
-                    mFacetHasMultipleAppsCache.get(index)  /* showMoreIcon */);
-        }
-
-        mCurrentFacetIndex = index;
-    }
-
-    /**
-     * Creates the View that is used for the buttons along the navigation bar.
-     *
-     * @param icon The icon to be used for the button.
-     */
-    private CarNavigationButton createNavButton(Drawable icon) {
-        CarNavigationButton button = (CarNavigationButton) View.inflate(mContext,
-                R.layout.car_navigation_button, null);
-        button.setResources(icon);
-        LinearLayout.LayoutParams lp =
-                new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1);
-        button.setLayoutParams(lp);
-
-        return button;
-    }
-
-    /**
-     * Initializes the click and long click listeners that correspond to the given command string.
-     * The click listeners are attached to the given button.
-     */
-    private void initClickListeners(View button, int index, String clickString,
-            String longPressString) {
-        // Each button at least have an action when pressed.
-        if (TextUtils.isEmpty(clickString)) {
-            throw new RuntimeException("Facet at index " + index + " does not have click action.");
-        }
-
-        try {
-            Intent intent = Intent.parseUri(clickString, Intent.URI_INTENT_SCHEME);
-            button.setOnClickListener(v -> onFacetClicked(intent, index));
-        } catch (URISyntaxException e) {
-            throw new RuntimeException("Malformed intent uri", e);
-        }
-
-        if (TextUtils.isEmpty(longPressString)) {
-            button.setLongClickable(false);
-            return;
-        }
-
-        try {
-            Intent intent = Intent.parseUri(longPressString, Intent.URI_INTENT_SCHEME);
-            button.setOnLongClickListener(v -> {
-                onFacetLongClicked(intent, index);
-                return true;
-            });
-        } catch (URISyntaxException e) {
-            throw new RuntimeException("Malformed long-press intent uri", e);
-        }
-    }
-
-    /**
-     * Handles a click on a facet. A click will trigger the given Intent.
-     *
-     * @param index The index of the facet that was clicked.
-     */
-    private void onFacetClicked(Intent intent, int index) {
-        String packageName = intent.getPackage();
-
-        if (packageName == null && !intent.getCategories().contains(Intent.CATEGORY_HOME)) {
-            return;
-        }
-
-        intent.putExtra(EXTRA_FACET_CATEGORIES, mFacetCategories.get(index));
-        intent.putExtra(EXTRA_FACET_PACKAGES, mFacetPackages.get(index));
-        // The facet is identified by the index in which it was added to the nav bar.
-        // This value can be used to determine which facet was selected
-        intent.putExtra(EXTRA_FACET_ID, Integer.toString(index));
-
-        // If the current facet is clicked, we want to launch the picker by default
-        // rather than the "preferred/last run" app.
-        intent.putExtra(EXTRA_FACET_LAUNCH_PICKER, index == mCurrentFacetIndex);
-
-        int windowingMode = WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
-        int activityType = ACTIVITY_TYPE_UNDEFINED;
-        if (intent.getCategories().contains(Intent.CATEGORY_HOME)) {
-            windowingMode = WINDOWING_MODE_UNDEFINED;
-            activityType = ACTIVITY_TYPE_HOME;
-        }
-
-        setCurrentFacet(index);
-        mStatusBar.startActivityOnStack(intent, windowingMode, activityType);
-    }
-
-    /**
-     * Handles a long-press on a facet. The long-press will trigger the given Intent.
-     *
-     * @param index The index of the facet that was clicked.
-     */
-    private void onFacetLongClicked(Intent intent, int index) {
-        setCurrentFacet(index);
-        mStatusBar.startActivityOnStack(intent,
-                WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_UNDEFINED);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
index e5a311d..1d9ef61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
@@ -16,17 +16,15 @@
 
 package com.android.systemui.statusbar.car;
 
+import android.app.UiModeManager;
 import android.content.Context;
-import android.graphics.Canvas;
 import android.util.AttributeSet;
-import android.view.MotionEvent;
+import android.util.Log;
 import android.view.View;
 import android.widget.LinearLayout;
 
+import com.android.keyguard.AlphaOptimizedImageButton;
 import com.android.systemui.R;
-import com.android.systemui.plugins.statusbar.phone.NavGesture;
-import com.android.systemui.statusbar.phone.NavigationBarGestureHelper;
-import com.android.systemui.statusbar.phone.NavigationBarView;
 
 /**
  * A custom navigation bar for the automotive use case.
@@ -34,9 +32,10 @@
  * The navigation bar in the automotive use case is more like a list of shortcuts, rendered
  * in a linear layout.
  */
-class CarNavigationBarView extends NavigationBarView {
+class CarNavigationBarView extends LinearLayout {
     private LinearLayout mNavButtons;
-    private LinearLayout mLightsOutButtons;
+    private AlphaOptimizedImageButton mNotificationsButton;
+    private CarStatusBar mCarStatusBar;
 
     public CarNavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -45,99 +44,16 @@
     @Override
     public void onFinishInflate() {
         mNavButtons = findViewById(R.id.nav_buttons);
-        mLightsOutButtons = findViewById(R.id.lights_out);
+
+        mNotificationsButton = findViewById(R.id.notifications);
+        mNotificationsButton.setOnClickListener(this::onNotificationsClick);
     }
 
-    public void addButton(CarNavigationButton button, CarNavigationButton lightsOutButton){
-        mNavButtons.addView(button);
-        mLightsOutButtons.addView(lightsOutButton);
+    void setStatusBar(CarStatusBar carStatusBar) {
+        mCarStatusBar = carStatusBar;
     }
 
-    @Override
-    public void setDisabledFlags(int disabledFlags, boolean force) {
-        // TODO: Populate.
-    }
-
-    @Override
-    public void reorient() {
-        // We expect the car head unit to always have a fixed rotation so we ignore this. The super
-        // class implentation expects mRotatedViews to be populated, so if you call into it, there
-        // is a possibility of a NullPointerException.
-    }
-
-    @Override
-    public View getCurrentView() {
-        return this;
-    }
-
-    @Override
-    public void setNavigationIconHints(int hints, boolean force) {
-        // We do not need to set the navigation icon hints for a vehicle
-        // Calling setNavigationIconHints in the base class will result in a NPE as the car
-        // navigation bar does not have a back button.
-    }
-
-    @Override
-    public void onPluginConnected(NavGesture plugin, Context context) {
-        // set to null version of the plugin ignoring incoming arg.
-        super.onPluginConnected(new NullNavGesture(), context);
-    }
-
-    @Override
-    public void onPluginDisconnected(NavGesture plugin) {
-        // reinstall the null nav gesture plugin
-        super.onPluginConnected(new NullNavGesture(), getContext());
-    }
-
-    /**
-     * Null object pattern to work around expectations of the base class.
-     * This is a temporary solution to have the car system ui working.
-     * Already underway is a refactor of they car sys ui as to not use this class
-     * hierarchy.
-     */
-    private static class NullNavGesture implements NavGesture {
-        @Override
-        public GestureHelper getGestureHelper() {
-            return new GestureHelper() {
-                @Override
-                public boolean onTouchEvent(MotionEvent event) {
-                    return false;
-                }
-
-                @Override
-                public boolean onInterceptTouchEvent(MotionEvent event) {
-                    return false;
-                }
-
-                @Override
-                public void setBarState(boolean vertical, boolean isRtl) {
-                }
-
-                @Override
-                public void onDraw(Canvas canvas) {
-                }
-
-                @Override
-                public void onDarkIntensityChange(float intensity) {
-                }
-
-                @Override
-                public void onLayout(boolean changed, int left, int top, int right, int bottom) {
-                }
-            };
-        }
-
-        @Override
-        public int getVersion() {
-            return 0;
-        }
-
-        @Override
-        public void onCreate(Context sysuiContext, Context pluginContext) {
-        }
-
-        @Override
-        public void onDestroy() {
-        }
+    protected void onNotificationsClick(View v) {
+        mCarStatusBar.togglePanel();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
index 2de358f..0cdaec1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationButton.java
@@ -1,72 +1,87 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 package com.android.systemui.statusbar.car;
 
 import android.content.Context;
-import android.graphics.drawable.Drawable;
+import android.content.Intent;
+import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.widget.ImageView;
-import android.widget.RelativeLayout;
 
-import com.android.keyguard.AlphaOptimizedImageButton;
 import com.android.systemui.R;
 
+import java.net.URISyntaxException;
+
 /**
- * A wrapper view for a car navigation facet, which includes a button icon and a drop down icon.
+ * CarNavigationButton is an image button that allows for a bit more configuration at the
+ * xml file level. This allows for more control via overlays instead of having to update
+ * code.
  */
-public class CarNavigationButton extends RelativeLayout {
+public class CarNavigationButton extends com.android.keyguard.AlphaOptimizedImageButton {
+
     private static final float SELECTED_ALPHA = 1;
     private static final float UNSELECTED_ALPHA = 0.7f;
 
-    private AlphaOptimizedImageButton mIcon;
-    private AlphaOptimizedImageButton mMoreIcon;
+    private Context mContext;
+    private String mIntent = null;
+    private String mLongIntent = null;
+    private boolean mBroadcastIntent = false;
+    private boolean mSelected = false;
+
 
     public CarNavigationButton(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mContext = context;
+        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarNavigationButton);
+        mIntent = typedArray.getString(R.styleable.CarNavigationButton_intent);
+        mLongIntent = typedArray.getString(R.styleable.CarNavigationButton_longIntent);
+        mBroadcastIntent = typedArray.getBoolean(R.styleable.CarNavigationButton_broadcast, false);
     }
 
+
+    /**
+     * After the standard inflate this then adds the xml defined intents to click and long click
+     * actions if defined.
+     */
     @Override
     public void onFinishInflate() {
         super.onFinishInflate();
-        mIcon = findViewById(R.id.car_nav_button_icon);
-        mIcon.setScaleType(ImageView.ScaleType.CENTER);
-        mIcon.setClickable(false);
-        mIcon.setBackgroundColor(android.R.color.transparent);
-        mIcon.setAlpha(UNSELECTED_ALPHA);
-
-        mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
-        mMoreIcon.setClickable(false);
-        mMoreIcon.setBackgroundColor(android.R.color.transparent);
-        mMoreIcon.setVisibility(INVISIBLE);
-        mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow));
-        mMoreIcon.setAlpha(UNSELECTED_ALPHA);
-    }
-
-    public void setResources(Drawable icon) {
-        mIcon.setImageDrawable(icon);
-    }
-
-    public void setSelected(boolean selected, boolean showMoreIcon) {
-        if (selected) {
-            mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : INVISIBLE);
-            mMoreIcon.setAlpha(SELECTED_ALPHA);
-            mIcon.setAlpha(SELECTED_ALPHA);
-        } else {
-            mMoreIcon.setVisibility(INVISIBLE);
-            mIcon.setAlpha(UNSELECTED_ALPHA);
+        setScaleType(ImageView.ScaleType.CENTER);
+        setAlpha(UNSELECTED_ALPHA);
+        try {
+            if (mIntent != null) {
+                final Intent intent = Intent.parseUri(mIntent, Intent.URI_INTENT_SCHEME);
+                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+                setOnClickListener(v -> {
+                    if (mBroadcastIntent) {
+                        mContext.sendBroadcast(intent);
+                        return;
+                    }
+                    mContext.startActivity(intent);
+                });
+            }
+        } catch (URISyntaxException e) {
+            throw new RuntimeException("Failed to attach intent", e);
         }
+
+        try {
+            if (mLongIntent != null) {
+                final Intent intent = Intent.parseUri(mLongIntent, Intent.URI_INTENT_SCHEME);
+                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
+                setOnLongClickListener(v -> {
+                    mContext.startActivity(intent);
+                    return true;
+                });
+            }
+        } catch (URISyntaxException e) {
+            throw new RuntimeException("Failed to attach long press intent", e);
+        }
+    }
+
+    /**
+     * @param selected true if should indicate if this is a selected state, false otherwise
+     */
+    public void setSelected(boolean selected) {
+        super.setSelected(selected);
+        mSelected = selected;
+        setAlpha(mSelected ? SELECTED_ALPHA : UNSELECTED_ALPHA);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 3dfb913..c15a013 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -18,17 +18,14 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.content.BroadcastReceiver;
-import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.service.notification.StatusBarNotification;
 import android.util.Log;
+import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
@@ -46,10 +43,7 @@
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
-import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.statusbar.ExpandableNotificationRow;
-import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
 import com.android.systemui.statusbar.phone.NavigationBarView;
@@ -69,7 +63,6 @@
 
     private TaskStackListenerImpl mTaskStackListener;
 
-    private CarNavigationBarController mController;
     private FullscreenUserSwitcher mFullscreenUserSwitcher;
 
     private CarBatteryController mCarBatteryController;
@@ -78,15 +71,23 @@
 
     private ConnectedDeviceSignalController mConnectedDeviceSignalController;
     private ViewGroup mNavigationBarWindow;
+    private ViewGroup mLeftNavigationBarWindow;
+    private ViewGroup mRightNavigationBarWindow;
     private CarNavigationBarView mNavigationBarView;
+    private CarNavigationBarView mLeftNavigationBarView;
+    private CarNavigationBarView mRightNavigationBarView;
 
     private final Object mQueueLock = new Object();
+    private boolean mShowLeft;
+    private boolean mShowRight;
+    private boolean mShowBottom;
+    private CarFacetButtonController mCarFacetButtonController;
+
     @Override
     public void start() {
         super.start();
         mTaskStackListener = new TaskStackListenerImpl();
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
-        registerPackageChangeReceivers();
 
         mStackScroller.setScrollingEnabled(true);
 
@@ -104,6 +105,16 @@
             mNavigationBarView = null;
         }
 
+        if (mLeftNavigationBarWindow != null) {
+            mWindowManager.removeViewImmediate(mLeftNavigationBarWindow);
+            mLeftNavigationBarView = null;
+        }
+
+        if (mRightNavigationBarWindow != null) {
+            mWindowManager.removeViewImmediate(mRightNavigationBarWindow);
+            mRightNavigationBarView = null;
+        }
+
         super.destroy();
     }
 
@@ -153,10 +164,36 @@
 
     @Override
     protected void createNavigationBar() {
+        mCarFacetButtonController = new CarFacetButtonController(mContext);
         if (mNavigationBarView != null) {
             return;
         }
 
+        mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
+        if (mShowBottom) {
+            buildBottomBar();
+        }
+
+        int widthForSides = mContext.getResources().getDimensionPixelSize(
+                R.dimen.navigation_bar_height_car_mode);
+
+
+        mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
+
+        if (mShowLeft) {
+            buildLeft(widthForSides);
+        }
+
+        mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
+
+        if (mShowRight) {
+            buildRight(widthForSides);
+        }
+
+    }
+
+
+    private void buildBottomBar() {
         // SystemUI requires that the navigation bar view have a parent. Since the regular
         // StatusBar inflates navigation_bar_window as this parent view, use the same view for the
         // CarNavigationBarView.
@@ -171,17 +208,15 @@
         mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0);
         if (mNavigationBarView == null) {
             Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+            throw new RuntimeException("Unable to build botom nav bar due to missing layout");
         }
+        mNavigationBarView.setStatusBar(this);
 
 
-        mController = new CarNavigationBarController(mContext, mNavigationBarView,
-                this /* ActivityStarter*/);
-        mNavigationBarView.getBarTransitions().setAlwaysOpaque(true);
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
-                WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
-                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                         | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                         | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
@@ -189,9 +224,74 @@
         lp.setTitle("CarNavigationBar");
         lp.windowAnimations = 0;
 
+
+        mCarFacetButtonController.addCarNavigationBar(mNavigationBarView);
         mWindowManager.addView(mNavigationBarWindow, lp);
     }
 
+    private void buildLeft(int widthForSides) {
+        mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext,
+                R.layout.navigation_bar_window, null);
+        if (mLeftNavigationBarWindow == null) {
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window");
+        }
+
+        View.inflate(mContext, R.layout.car_left_navigation_bar, mLeftNavigationBarWindow);
+        mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0);
+        if (mLeftNavigationBarView == null) {
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+            throw new RuntimeException("Unable to build left nav bar due to missing layout");
+        }
+        mLeftNavigationBarView.setStatusBar(this);
+        mCarFacetButtonController.addCarNavigationBar(mLeftNavigationBarView);
+
+        WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams(
+                widthForSides, LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+                PixelFormat.TRANSLUCENT);
+        leftlp.setTitle("LeftCarNavigationBar");
+        leftlp.windowAnimations = 0;
+        leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
+        leftlp.gravity = Gravity.LEFT;
+        mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
+    }
+
+
+    private void buildRight(int widthForSides) {
+        mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext,
+                R.layout.navigation_bar_window, null);
+        if (mRightNavigationBarWindow == null) {
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window");
+        }
+
+        View.inflate(mContext, R.layout.car_right_navigation_bar, mRightNavigationBarWindow);
+        mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0);
+        if (mRightNavigationBarView == null) {
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+            throw new RuntimeException("Unable to build right nav bar due to missing layout");
+        }
+        mRightNavigationBarView.setStatusBar(this);
+        mCarFacetButtonController.addCarNavigationBar(mRightNavigationBarView);
+
+        WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams(
+                widthForSides, LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+                PixelFormat.TRANSLUCENT);
+        rightlp.setTitle("RightCarNavigationBar");
+        rightlp.windowAnimations = 0;
+        rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
+        rightlp.gravity = Gravity.RIGHT;
+        mWindowManager.addView(mRightNavigationBarWindow, rightlp);
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         //When executing dump() funciton simultaneously, we need to serialize them
@@ -204,8 +304,8 @@
         }
 
         pw.print("  mTaskStackListener="); pw.println(mTaskStackListener);
-        pw.print("  mController=");
-        pw.println(mController);
+        pw.print("  mCarFacetButtonController=");
+        pw.println(mCarFacetButtonController);
         pw.print("  mFullscreenUserSwitcher="); pw.println(mFullscreenUserSwitcher);
         pw.print("  mCarBatteryController=");
         pw.println(mCarBatteryController);
@@ -229,10 +329,6 @@
         }
     }
 
-    @Override
-    public NavigationBarView getNavigationBarView() {
-        return mNavigationBarView;
-    }
 
     @Override
     public View getNavigationBarWindow() {
@@ -269,24 +365,6 @@
         }
     }
 
-    private BroadcastReceiver mPackageChangeReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent.getData() == null || mController == null) {
-                return;
-            }
-            String packageName = intent.getData().getSchemeSpecificPart();
-            mController.onPackageChange(packageName);
-        }
-    };
-
-    private void registerPackageChangeReceivers() {
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addDataScheme("package");
-        mContext.registerReceiver(mPackageChangeReceiver, filter);
-    }
 
     public boolean hasDockedTask() {
         return Recents.getSystemServices().hasDockedTask();
@@ -301,10 +379,7 @@
         public void onTaskStackChanged() {
             ActivityManager.RunningTaskInfo runningTaskInfo =
                     ActivityManagerWrapper.getInstance().getRunningTask();
-            if (runningTaskInfo != null && runningTaskInfo.baseActivity != null) {
-                mController.taskChanged(runningTaskInfo.baseActivity.getPackageName(),
-                        runningTaskInfo);
-            }
+            mCarFacetButtonController.taskChanged(runningTaskInfo);
         }
     }
 
@@ -346,33 +421,6 @@
         // Do nothing, we don't want to display media art in the lock screen for a car.
     }
 
-    private int startActivityWithOptions(Intent intent, Bundle options) {
-        int result = ActivityManager.START_CANCELED;
-        try {
-            result = ActivityManager.getService().startActivityAsUser(null /* caller */,
-                    mContext.getBasePackageName(),
-                    intent,
-                    intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                    null /* resultTo*/,
-                    null /* resultWho*/,
-                    0 /* requestCode*/,
-                    Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP,
-                    null /* profilerInfo*/,
-                    options,
-                    UserHandle.CURRENT.getIdentifier());
-        } catch (RemoteException e) {
-            Log.w(TAG, "Unable to start activity", e);
-        }
-
-        return result;
-    }
-
-    public int startActivityOnStack(Intent intent, int windowingMode, int activityType) {
-        final ActivityOptions options = ActivityOptions.makeBasic();
-        options.setLaunchWindowingMode(windowingMode);
-        options.setLaunchActivityType(activityType);
-        return startActivityWithOptions(intent, options.toBundle());
-    }
 
     @Override
     public void animateExpandNotificationsPanel() {
@@ -390,8 +438,6 @@
     @Override
     public void onDensityOrFontScaleChanged() {
         super.onDensityOrFontScaleChanged();
-        mController.onDensityOrFontScaleChanged();
-
         // Need to update the background on density changed in case the change was due to night
         // mode.
         mNotificationPanelBackground = getDefaultWallpaper();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index 11d20b2..ef44ad1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -77,11 +77,11 @@
         mStatusBar = statusBar;
     }
 
-    public ActivityOptions getLaunchAnimation(
-            ExpandableNotificationRow sourceNofitication) {
-        AnimationRunner animationRunner = new AnimationRunner(sourceNofitication);
-        return ActivityOptions.makeRemoteAnimation(
-                new RemoteAnimationAdapter(animationRunner, 1000 /* Duration */, 0 /* delay */));
+    public RemoteAnimationAdapter getLaunchAnimation(
+            ExpandableNotificationRow sourceNotification) {
+        AnimationRunner animationRunner = new AnimationRunner(sourceNotification);
+        return new RemoteAnimationAdapter(animationRunner, ANIMATION_DURATION,
+                0 /* statusBarTransitionDelay */);
     }
 
     public boolean isAnimationPending() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 72938c2..79c605e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -19,6 +19,7 @@
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.windowStateToString;
 
+import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
 import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
 import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
@@ -71,7 +72,6 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
-import android.widget.Button;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -165,6 +165,11 @@
         public void onRecentsAnimationStarted() {
             mNavigationBarView.setRecentsAnimationStarted(true);
         }
+
+        @Override
+        public void onInteractionFlagsChanged(@InteractionType int flags) {
+            mNavigationBarView.updateStates();
+        }
     };
 
     // ----- Fragment Lifecycle Callbacks -----
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
index 63f2ceb..8970ea0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java
@@ -127,7 +127,7 @@
 
     private boolean proxyMotionEvents(MotionEvent event) {
         final IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
-        if (overviewProxy != null) {
+        if (overviewProxy != null && mNavigationBarView.isQuickStepSwipeUpEnabled()) {
             mNavigationBarView.requestUnbufferedDispatch(event);
             event.transform(mTransformGlobalMatrix);
             try {
@@ -192,7 +192,7 @@
     }
 
     public void onDraw(Canvas canvas) {
-        if (mOverviewProxyService.getProxy() != null) {
+        if (mNavigationBarView.isQuickScrubEnabled()) {
             mQuickScrubController.onDraw(canvas);
         }
     }
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 53dc814..cd4eb23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -40,6 +40,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.support.annotation.ColorInt;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -76,6 +77,11 @@
 import java.io.PrintWriter;
 import java.util.function.Consumer;
 
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB;
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_HIDE_BACK_BUTTON;
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
+
 public class NavigationBarView extends FrameLayout implements PluginListener<NavGesture> {
     final static boolean DEBUG = false;
     final static String TAG = "StatusBar/NavBarView";
@@ -372,6 +378,19 @@
         return getRecentsButton().getVisibility() == View.VISIBLE;
     }
 
+    public boolean isQuickStepSwipeUpEnabled() {
+        return mOverviewProxyService.getProxy() != null
+                && ((mOverviewProxyService.getInteractionFlags()
+                        & FLAG_DISABLE_SWIPE_UP) == 0);
+    }
+
+    public boolean isQuickScrubEnabled() {
+        return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true)
+                && mOverviewProxyService.getProxy() != null && !isRecentsButtonVisible()
+                && ((mOverviewProxyService.getInteractionFlags()
+                        & FLAG_DISABLE_QUICK_SCRUB) == 0);
+    }
+
     private void updateCarModeIcons(Context ctx) {
         mBackCarModeIcon = getDrawable(ctx,
                 R.drawable.ic_sysbar_back_carmode, R.drawable.ic_sysbar_back_carmode);
@@ -391,20 +410,20 @@
         }
         if (oldConfig.densityDpi != newConfig.densityDpi
                 || oldConfig.getLayoutDirection() != newConfig.getLayoutDirection()) {
-            final boolean proxyAvailable = mOverviewProxyService.getProxy() != null;
-            mBackIcon = proxyAvailable
+            final boolean quickStepEnabled = isQuickStepSwipeUpEnabled() || isQuickScrubEnabled();
+            mBackIcon = quickStepEnabled
                     ? getDrawable(ctx, R.drawable.ic_sysbar_back_quick_step,
                             R.drawable.ic_sysbar_back_quick_step_dark)
                     : getDrawable(ctx, R.drawable.ic_sysbar_back, R.drawable.ic_sysbar_back_dark);
             mBackLandIcon = mBackIcon;
-            mBackAltIcon = proxyAvailable
+            mBackAltIcon = quickStepEnabled
                     ? getDrawable(ctx, R.drawable.ic_sysbar_back_ime_quick_step,
                             R.drawable.ic_sysbar_back_ime_quick_step_dark)
                     : getDrawable(ctx, R.drawable.ic_sysbar_back_ime,
                             R.drawable.ic_sysbar_back_ime_dark);
             mBackAltLandIcon = mBackAltIcon;
 
-            mHomeDefaultIcon = proxyAvailable
+            mHomeDefaultIcon = quickStepEnabled
                     ? getDrawable(ctx, R.drawable.ic_sysbar_home_quick_step,
                             R.drawable.ic_sysbar_home_quick_step_dark)
                     : getDrawable(ctx, R.drawable.ic_sysbar_home, R.drawable.ic_sysbar_home_dark);
@@ -555,9 +574,14 @@
             disableBack = false;
             disableRecent = false;
         }
+
         if (mOverviewProxyService.getProxy() != null) {
-            // When overview is connected to the launcher service, disable the recents button
-            disableRecent = true;
+            // When overview is connected to the launcher service, disable the recents button by
+            // default unless overwritten by interaction flags. Similar with the back button but
+            // shown by default.
+            final int flags = mOverviewProxyService.getInteractionFlags();
+            disableRecent |= (flags & FLAG_SHOW_OVERVIEW_BUTTON) == 0;
+            disableBack |= (flags & FLAG_HIDE_BACK_BUTTON) != 0;
         }
 
         ViewGroup navButtons = (ViewGroup) getCurrentView().findViewById(R.id.nav_buttons);
@@ -635,6 +659,11 @@
         updateSlippery();
     }
 
+    public void updateStates() {
+        updateSlippery();
+        setDisabledFlags(mDisabledFlags, true);
+    }
+
     private void updateSlippery() {
         setSlippery(mOverviewProxyService.getProxy() != null && mPanelView.isFullyExpanded());
     }
@@ -794,9 +823,8 @@
     }
 
     public void onOverviewProxyConnectionChanged(boolean isConnected) {
-        setSlippery(!isConnected);
-        setDisabledFlags(mDisabledFlags, true);
-        setUpSwipeUpOnboarding(isConnected);
+        updateStates();
+        setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
         updateIcons(getContext(), Configuration.EMPTY, mConfiguration);
         setNavigationIconHints(mNavigationIconHints, true);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 6444cc8..747a551 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -575,7 +575,7 @@
 
         Intent browserIntent = getTaskIntent(taskId, userId);
         Notification.Builder builder = new Notification.Builder(mContext, NotificationChannels.GENERAL);
-        if (browserIntent != null) {
+        if (browserIntent != null && browserIntent.isWebIntent()) {
             // Make sure that this doesn't resolve back to an instant app
             browserIntent.setComponent(null)
                     .setPackage(null)
@@ -597,8 +597,9 @@
                     .addCategory("unique:" + System.currentTimeMillis())
                     .putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName)
                     .putExtra(Intent.EXTRA_VERSION_CODE, (int) (appInfo.versionCode & 0x7fffffff))
-                    .putExtra(Intent.EXTRA_VERSION_CODE, appInfo.versionCode)
-                    .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent);
+                    .putExtra(Intent.EXTRA_LONG_VERSION_CODE, appInfo.versionCode)
+                    .putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent)
+                    .putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, pendingIntent);
 
             PendingIntent webPendingIntent = PendingIntent.getActivity(mContext, 0, goToWebIntent, 0);
             Action webAction = new Notification.Action.Builder(null, mContext.getString(R.string.go_to_web),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
index f4da0c3..0bf01b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
@@ -29,15 +29,12 @@
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.RemoteException;
-import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Slog;
-import android.view.Display;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
@@ -138,8 +135,9 @@
         new GestureDetector.SimpleOnGestureListener() {
             @Override
             public boolean onFling(MotionEvent e1, MotionEvent e2, float velX, float velY) {
-                if (!isQuickScrubEnabled() || mQuickScrubActive || !mAllowQuickSwitch ||
-                        mNavigationBarView.getDownHitTarget() != HIT_TARGET_HOME) {
+                if (!mNavigationBarView.isQuickScrubEnabled() || mQuickScrubActive
+                        || !mAllowQuickSwitch
+                        || mNavigationBarView.getDownHitTarget() != HIT_TARGET_HOME) {
                     return false;
                 }
                 float velocityX = mIsRTL ? -velX : velX;
@@ -196,9 +194,8 @@
      */
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
-        final IOverviewProxy overviewProxy = mOverviewEventSender.getProxy();
         final ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();
-        if (overviewProxy == null) {
+        if (!mNavigationBarView.isQuickScrubEnabled()) {
             homeButton.setDelayTouchFeedback(false);
             return false;
         }
@@ -228,7 +225,7 @@
                 int x = (int) event.getX();
                 int y = (int) event.getY();
                 mHomeButtonView = homeButton.getCurrentView();
-                if (isQuickScrubEnabled()
+                if (mNavigationBarView.isQuickScrubEnabled()
                         && mNavigationBarView.getDownHitTarget() == HIT_TARGET_HOME) {
                     mTouchDownX = x;
                     mTouchDownY = y;
@@ -296,7 +293,7 @@
                             : Utilities.clamp(offset - mDownOffset, 0, trackSize);
                         if (mQuickScrubActive) {
                             try {
-                                overviewProxy.onQuickScrubProgress(scrubFraction);
+                                mOverviewEventSender.getProxy().onQuickScrubProgress(scrubFraction);
                                 if (DEBUG_OVERVIEW_PROXY) {
                                     Log.d(TAG_OPS, "Quick Scrub Progress:" + scrubFraction);
                                 }
@@ -377,10 +374,6 @@
         }
     }
 
-    boolean isQuickScrubEnabled() {
-        return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true);
-    }
-
     private void startQuickScrub() {
         if (!mQuickScrubActive) {
             mQuickScrubActive = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 94ebc1b..2b50853 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -138,8 +138,7 @@
     protected float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD;
     protected float mScrimBehindAlphaUnlocking = SCRIM_BEHIND_ALPHA_UNLOCKING;
 
-    // Assuming the shade is expanded during initialization
-    private float mExpansionFraction = 1f;
+    private float mFraction;
 
     private boolean mDarkenWhileDragging;
     protected boolean mAnimateChange;
@@ -253,7 +252,6 @@
         mCurrentBehindTint = state.getBehindTint();
         mCurrentInFrontAlpha = state.getFrontAlpha();
         mCurrentBehindAlpha = state.getBehindAlpha();
-        applyExpansionToAlpha();
 
         // Cancel blanking transitions that were pending before we requested a new state
         if (mPendingFrameCallback != null) {
@@ -365,50 +363,45 @@
      * @param fraction From 0 to 1 where 0 means collapse and 1 expanded.
      */
     public void setPanelExpansion(float fraction) {
-        if (mExpansionFraction != fraction) {
-            mExpansionFraction = fraction;
+        if (mFraction != fraction) {
+            mFraction = fraction;
 
-            if (!(mState == ScrimState.UNLOCKED || mState == ScrimState.KEYGUARD)) {
-                return;
-            }
+            if (mState == ScrimState.UNLOCKED) {
+                // Darken scrim as you pull down the shade when unlocked
+                float behindFraction = getInterpolatedFraction();
+                behindFraction = (float) Math.pow(behindFraction, 0.8f);
+                mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard;
+                mCurrentInFrontAlpha = 0;
+            } else if (mState == ScrimState.KEYGUARD) {
+                if (mUpdatePending) {
+                    return;
+                }
 
-            applyExpansionToAlpha();
-
-            if (mUpdatePending) {
+                // Either darken of make the scrim transparent when you
+                // pull down the shade
+                float interpolatedFract = getInterpolatedFraction();
+                if (mDarkenWhileDragging) {
+                    mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking,
+                            mScrimBehindAlphaKeyguard, interpolatedFract);
+                    mCurrentInFrontAlpha = (1f - interpolatedFract) * SCRIM_IN_FRONT_ALPHA_LOCKED;
+                } else {
+                    mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, mScrimBehindAlphaKeyguard,
+                            interpolatedFract);
+                    mCurrentInFrontAlpha = 0;
+                }
+            } else {
                 return;
             }
 
             if (mPinnedHeadsUpCount != 0) {
                 updateHeadsUpScrim(false);
             }
+
             updateScrim(false /* animate */, mScrimInFront, mCurrentInFrontAlpha);
             updateScrim(false /* animate */, mScrimBehind, mCurrentBehindAlpha);
         }
     }
 
-    private void applyExpansionToAlpha() {
-        if (mState == ScrimState.UNLOCKED) {
-            // Darken scrim as you pull down the shade when unlocked
-            float behindFraction = getInterpolatedFraction();
-            behindFraction = (float) Math.pow(behindFraction, 0.8f);
-            mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard;
-            mCurrentInFrontAlpha = 0;
-        } else if (mState == ScrimState.KEYGUARD) {
-            // Either darken of make the scrim transparent when you
-            // pull down the shade
-            float interpolatedFract = getInterpolatedFraction();
-            if (mDarkenWhileDragging) {
-                mCurrentBehindAlpha = MathUtils.lerp(mScrimBehindAlphaUnlocking,
-                        mScrimBehindAlphaKeyguard, interpolatedFract);
-                mCurrentInFrontAlpha = (1f - interpolatedFract) * SCRIM_IN_FRONT_ALPHA_LOCKED;
-            } else {
-                mCurrentBehindAlpha = MathUtils.lerp(0 /* start */, mScrimBehindAlphaKeyguard,
-                        interpolatedFract);
-                mCurrentInFrontAlpha = 0;
-            }
-        }
-    }
-
     /**
      * Keyguard and shade scrim opacity varies according to how many notifications are visible.
      * @param notificationCount Number of visible notifications.
@@ -504,7 +497,7 @@
     }
 
     private float getInterpolatedFraction() {
-        float frac = mExpansionFraction;
+        float frac = mFraction;
         // let's start this 20% of the way down the screen
         frac = frac * 1.2f - 0.2f;
         if (frac <= 0) {
@@ -834,7 +827,7 @@
         } else {
             alpha = 1.0f - mTopHeadsUpDragAmount;
         }
-        float expandFactor = (1.0f - mExpansionFraction);
+        float expandFactor = (1.0f - mFraction);
         expandFactor = Math.max(expandFactor, 0.0f);
         return alpha * expandFactor;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 3b63d6c..24920cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -111,6 +111,7 @@
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
+import android.view.RemoteAnimationAdapter;
 import android.view.ThreadedRenderer;
 import android.view.View;
 import android.view.ViewGroup;
@@ -2849,7 +2850,7 @@
                     Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
             int result = ActivityManager.START_CANCELED;
             ActivityOptions options = new ActivityOptions(getActivityOptions(
-                    null /* sourceNotification */));
+                    null /* remoteAnimation */));
             options.setDisallowEnterPictureInPictureWhileLaunching(
                     disallowEnterPictureInPictureWhileLaunching);
             if (intent == KeyguardBottomAreaView.INSECURE_CAMERA_INTENT) {
@@ -5001,11 +5002,15 @@
                         fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT,
                                 remoteInputText.toString());
                     }
+                    RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(
+                            row);
                     try {
+                        ActivityManager.getService().registerRemoteAnimationForNextActivityStart(
+                                intent.getCreatorPackage(), adapter);
                         launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
-                                null, null, getActivityOptions(row));
+                                null, null, getActivityOptions(adapter));
                         mActivityLaunchAnimator.setLaunchResult(launchResult);
-                    } catch (PendingIntent.CanceledException e) {
+                    } catch (RemoteException | PendingIntent.CanceledException e) {
                         // the stack trace isn't very helpful here.
                         // Just log the exception message.
                         Log.w(TAG, "Sending contentIntent failed: " + e);
@@ -5165,7 +5170,8 @@
             AsyncTask.execute(() -> {
                 int launchResult = TaskStackBuilder.create(mContext)
                         .addNextIntentWithParentStack(intent)
-                        .startActivities(getActivityOptions(row),
+                        .startActivities(getActivityOptions(
+                                mActivityLaunchAnimator.getLaunchAnimation(row)),
                                 new UserHandle(UserHandle.getUserId(appUid)));
                 mActivityLaunchAnimator.setLaunchResult(launchResult);
                 if (shouldCollapse()) {
@@ -5300,7 +5306,7 @@
                 }
                 try {
                     intent.send(null, 0, null, null, null, null, getActivityOptions(
-                            null /* sourceNotification */));
+                            null /* animationAdapter */));
                 } catch (PendingIntent.CanceledException e) {
                     // the stack trace isn't very helpful here.
                     // Just log the exception message.
@@ -5328,10 +5334,10 @@
         return true;
     }
 
-    protected Bundle getActivityOptions(ExpandableNotificationRow sourceNotification) {
+    protected Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) {
         ActivityOptions options;
-        if (sourceNotification != null) {
-            options = mActivityLaunchAnimator.getLaunchAnimation(sourceNotification);
+        if (animationAdapter != null) {
+            options = ActivityOptions.makeRemoteAnimation(animationAdapter);
         } else {
             options = ActivityOptions.makeBasic();
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 1c9c794..6764634 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -37,6 +37,7 @@
 import android.content.Intent;
 import android.metrics.LogMaker;
 import android.support.test.filters.SmallTest;
+import android.support.test.InstrumentationRegistry;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
@@ -73,6 +74,7 @@
         mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
         mHost = mock(QSTileHost.class);
         when(mHost.indexOf(spec)).thenReturn(POSITION);
+        when(mHost.getContext()).thenReturn(mContext.getBaseContext());
 
         mTile = spy(new TileImpl(mHost));
         mTile.mHandler = mTile.new H(mTestableLooper.getLooper());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 4702793..43e16db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -180,7 +180,6 @@
 
     @Test
     public void transitionToUnlocked() {
-        mScrimController.setPanelExpansion(0f);
         mScrimController.transitionTo(ScrimState.UNLOCKED);
         mScrimController.finishAnimationsImmediately();
         // Front scrim should be transparent
@@ -198,7 +197,6 @@
     public void transitionToUnlockedFromAod() {
         // Simulate unlock with fingerprint
         mScrimController.transitionTo(ScrimState.AOD);
-        mScrimController.setPanelExpansion(0f);
         mScrimController.finishAnimationsImmediately();
         mScrimController.transitionTo(ScrimState.UNLOCKED);
         // Immediately tinted after the transition starts
@@ -326,23 +324,6 @@
         verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
     }
 
-    @Test
-    public void testConservesExpansionOpacityAfterTransition() {
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
-        mScrimController.setPanelExpansion(0.5f);
-        mScrimController.finishAnimationsImmediately();
-
-        final float expandedAlpha = mScrimBehind.getViewAlpha();
-
-        mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
-        mScrimController.finishAnimationsImmediately();
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
-        mScrimController.finishAnimationsImmediately();
-
-        Assert.assertEquals("Scrim expansion opacity wasn't conserved when transitioning back",
-                expandedAlpha, mScrimBehind.getViewAlpha(), 0.01f);
-    }
-
     private void assertScrimTint(ScrimView scrimView, boolean tinted) {
         final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT;
         final String name = scrimView == mScrimInFront ? "front" : "back";
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index ae5e133..320c37f 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5213,6 +5213,50 @@
     // OS: P
     ACTION_OUTPUT_CHOOSER_DISCONNECT = 1297;
 
+    // OPEN: TV Settings > Home theater control
+    // OS: P
+    SETTINGS_TV_HOME_THEATER_CONTROL_CATEGORY = 1298;
+
+    // OPEN: TV Settings > TV Inputs (Inputs & Devices)
+    // OS: P
+    SETTINGS_TV_INPUTS_CATEGORY = 1299;
+
+    // OPEN: TV Settings > Device
+    // OS: P
+    SETTINGS_TV_DEVICE_CATEGORY = 1300;
+
+    // OPEN: TV Settings > Network > Proxy settings
+    // OS: P
+    DIALOG_TV_NETWORK_PROXY = 1301;
+
+    // Events for battery saver turning on/off and/or the interactive state changes.
+    // OS: P
+    BATTERY_SAVER = 1302;
+
+    // Device interactive state -- i.e. the screen ON (=1) or OFF (=1)
+    // OS: P
+    FIELD_INTERACTIVE = 1303;
+
+    // Time spent in milliseconds in the current mode.
+    // OS: P
+    FIELD_DURATION_MILLIS = 1304;
+
+    // Battery level in uA (0 - ~3,000,000 depending on device) when the current "mode" started.
+    // OS: P
+    FIELD_START_BATTERY_UA = 1305;
+
+    // Battery level in uA (0 - ~3,000,000 depending on device) when this event was created.
+    // OS: P
+    FIELD_END_BATTERY_UA = 1306;
+
+    // Battery level in % (0-100) when the current "mode" started.
+    // OS: P
+    FIELD_START_BATTERY_PERCENT = 1307;
+
+    // Battery level in % (0-100) when this event was created.
+    // OS: P
+    FIELD_END_BATTERY_PERCENT = 1308;
+
     // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 52ab85c..6c6dd5b 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -402,9 +402,9 @@
                     com.android.internal.R.dimen.config_screen_magnification_scaling_threshold,
                     scaleValue, false);
             mScalingThreshold = scaleValue.getFloat();
-            mScaleGestureDetector = new ScaleGestureDetector(context, this);
+            mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain());
             mScaleGestureDetector.setQuickScaleEnabled(false);
-            mScrollGestureDetector = new GestureDetector(context, this);
+            mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain());
         }
 
         @Override
@@ -638,7 +638,7 @@
 
         @VisibleForTesting boolean mShortcutTriggered;
 
-        @VisibleForTesting Handler mHandler = new Handler(this);
+        @VisibleForTesting Handler mHandler = new Handler(Looper.getMainLooper(), this);
 
         public DetectingState(Context context) {
             mLongTapMinDelay = ViewConfiguration.getLongPressTimeout();
@@ -654,7 +654,9 @@
             final int type = message.what;
             switch (type) {
                 case MESSAGE_ON_TRIPLE_TAP_AND_HOLD: {
-                    onTripleTapAndHold(/* down */ (MotionEvent) message.obj);
+                    MotionEvent down = (MotionEvent) message.obj;
+                    transitionToViewportDraggingStateAndClear(down);
+                    down.recycle();
                 }
                 break;
                 case MESSAGE_TRANSITION_TO_DELEGATING_STATE: {
@@ -720,8 +722,7 @@
                         // over insta-delegating on 3tap&swipe
                         // (which is a rare combo to be used aside from magnification)
                         if (isMultiTapTriggered(2 /* taps */)) {
-                            transitionTo(mViewportDraggingState);
-                            clear();
+                            transitionToViewportDraggingStateAndClear(event);
                         } else {
                             transitionToDelegatingStateAndClear();
                         }
@@ -806,7 +807,8 @@
         /** -> {@link ViewportDraggingState} */
         public void afterLongTapTimeoutTransitionToDraggingState(MotionEvent event) {
             mHandler.sendMessageDelayed(
-                    mHandler.obtainMessage(MESSAGE_ON_TRIPLE_TAP_AND_HOLD, event),
+                    mHandler.obtainMessage(MESSAGE_ON_TRIPLE_TAP_AND_HOLD,
+                            MotionEvent.obtain(event)),
                     ViewConfiguration.getLongPressTimeout());
         }
 
@@ -890,7 +892,7 @@
             }
         }
 
-        void onTripleTapAndHold(MotionEvent down) {
+        void transitionToViewportDraggingStateAndClear(MotionEvent down) {
 
             if (DEBUG_DETECTING) Slog.i(LOG_TAG, "onTripleTapAndHold()");
             clear();
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index ebb5040..4b3abea 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -24,7 +24,6 @@
 import static com.android.server.autofill.Helper.sPartitionMaxCount;
 import static com.android.server.autofill.Helper.sVerbose;
 
-import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -56,7 +55,12 @@
 import android.provider.Settings;
 import android.service.autofill.FillEventHistory;
 import android.service.autofill.UserData;
+import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.LocalLog;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -84,6 +88,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Entry point service for autofill management.
@@ -98,6 +103,8 @@
 
     static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
 
+    private static final char COMPAT_PACKAGE_DELIMITER = ':';
+
     private final Context mContext;
     private final AutoFillUI mUi;
 
@@ -123,6 +130,9 @@
     private final LocalLog mUiLatencyHistory = new LocalLog(20);
     private final LocalLog mWtfHistory = new LocalLog(50);
 
+    private final AutofillCompatState mAutofillCompatState = new AutofillCompatState();
+    private final LocalService mLocalService = new LocalService();
+
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -271,7 +281,7 @@
     @Override
     public void onStart() {
         publishBinderService(AUTOFILL_MANAGER_SERVICE, new AutoFillManagerServiceStub());
-        publishLocalService(AutofillManagerInternal.class, new LocalService());
+        publishLocalService(AutofillManagerInternal.class, mLocalService);
     }
 
     @Override
@@ -317,6 +327,11 @@
                     mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi,
                     mDisabledUsers.get(resolvedUserId));
             mServicesCache.put(userId, service);
+            final ArrayMap<String, Pair<Long, String>> compatPackages =
+                    service.getCompatibilityPackagesLocked();
+            if (compatPackages != null) {
+                addCompatibilityModeRequests(compatPackages, userId);
+            }
         }
         return service;
     }
@@ -482,6 +497,7 @@
         if (service != null) {
             mServicesCache.delete(userId);
             service.destroyLocked();
+            mAutofillCompatState.removeCompatibilityModeRequests(userId);
         }
     }
 
@@ -498,18 +514,60 @@
      */
     @GuardedBy("mLock")
     private void updateCachedServiceLocked(int userId, boolean disabled) {
-        AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
+        AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
         if (service != null) {
             service.destroySessionsLocked();
             service.updateLocked(disabled);
             if (!service.isEnabledLocked()) {
                 removeCachedServiceLocked(userId);
+            } else {
+                final ArrayMap<String, Pair<Long, String>> compatPackages =
+                        service.getCompatibilityPackagesLocked();
+                if (compatPackages != null) {
+                    addCompatibilityModeRequests(compatPackages, userId);
+                }
             }
         }
     }
 
-    private final class LocalService extends AutofillManagerInternal {
+    private void addCompatibilityModeRequests(
+            @NonNull ArrayMap<String, Pair<Long, String>> compatPackages, int userId) {
+        final Set<String> whiteListedPackages = Build.IS_ENG ? null
+                : getWhitelistedCompatModePackages();
+        final int compatPackageCount = compatPackages.size();
+        for (int i = 0; i < compatPackageCount; i++) {
+            final String packageName = compatPackages.keyAt(i);
+            if (!Build.IS_ENG && (whiteListedPackages == null
+                    || !whiteListedPackages.contains(packageName))) {
+                Slog.w(TAG, "Ignoring not whitelisted compat package " + packageName);
+                continue;
+            }
+            final Long maxVersionCode = compatPackages.valueAt(i).first;
+            if (maxVersionCode != null) {
+                mAutofillCompatState.addCompatibilityModeRequest(packageName,
+                        maxVersionCode, userId);
+            }
+        }
+    }
 
+    private @Nullable Set<String> getWhitelistedCompatModePackages() {
+        final String compatPackagesSetting = Settings.Global.getString(
+                mContext.getContentResolver(),
+                Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES);
+        if (TextUtils.isEmpty(compatPackagesSetting)) {
+            return null;
+        }
+        final Set<String> compatPackages = new ArraySet<>();
+        final SimpleStringSplitter splitter = new SimpleStringSplitter(
+                COMPAT_PACKAGE_DELIMITER);
+        splitter.setString(compatPackagesSetting);
+        while (splitter.hasNext()) {
+            compatPackages.add(splitter.next());
+        }
+        return compatPackages;
+    }
+
+    private final class LocalService extends AutofillManagerInternal {
         @Override
         public void onBackKeyPressed() {
             if (sDebug) Slog.d(TAG, "onBackKeyPressed()");
@@ -519,13 +577,59 @@
         @Override
         public boolean isCompatibilityModeRequested(@NonNull String packageName,
                 long versionCode, @UserIdInt int userId) {
+            return mAutofillCompatState.isCompatibilityModeRequested(
+                    packageName, versionCode, userId);
+        }
+    }
+
+    private static class AutofillCompatState {
+        private final Object mLock = new Object();
+
+        @GuardedBy("mLock")
+        private SparseArray<ArrayMap<String, Long>> mUserSpecs;
+
+        boolean isCompatibilityModeRequested(@NonNull String packageName,
+                long versionCode, @UserIdInt int userId) {
             synchronized (mLock) {
-                final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
-                if (service != null) {
-                    return service.isCompatibilityModeRequestedLocked(packageName, versionCode);
+                if (mUserSpecs == null) {
+                    return false;
+                }
+                final ArrayMap<String, Long> userSpec = mUserSpecs.get(userId);
+                if (userSpec == null) {
+                    return false;
+                }
+                final Long maxVersionCode = userSpec.get(packageName);
+                if (maxVersionCode == null) {
+                    return false;
+                }
+                return versionCode <= maxVersionCode;
+            }
+        }
+
+        void addCompatibilityModeRequest(@NonNull String packageName,
+                long versionCode, @UserIdInt int userId) {
+            synchronized (mLock) {
+                if (mUserSpecs == null) {
+                    mUserSpecs = new SparseArray<>();
+                }
+                ArrayMap<String, Long> userSpec = mUserSpecs.get(userId);
+                if (userSpec == null) {
+                    userSpec = new ArrayMap<>();
+                    mUserSpecs.put(userId, userSpec);
+                }
+                userSpec.put(packageName, versionCode);
+            }
+        }
+
+        void removeCompatibilityModeRequests(@UserIdInt int userId) {
+            synchronized (mLock) {
+                if (mUserSpecs != null) {
+                    mUserSpecs.remove(userId);
+                    if (mUserSpecs.size() <= 0) {
+                        mUserSpecs = null;
+                    }
                 }
             }
-            return false;
         }
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 75ae2dc..712ea36 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -64,6 +64,7 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.LocalLog;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
@@ -869,7 +870,7 @@
             }
             mUserData = userData;
             // Log it
-            int numberFields = mUserData == null ? 0: mUserData.getRemoteIds().length;
+            int numberFields = mUserData == null ? 0: mUserData.getCategoryIds().length;
             mMetricsLogger.write(Helper.newLogMaker(MetricsEvent.AUTOFILL_USERDATA_UPDATED,
                     getServicePackageName(), null)
                     .setCounterValue(numberFields));
@@ -906,7 +907,10 @@
         pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
         pw.print(prefix); pw.print("Field classification enabled: ");
             pw.println(isFieldClassificationEnabledLocked());
-        pw.print(prefix); pw.print("Compat pkgs: "); pw.println(getWhitelistedCompatModePackages());
+        final ArrayMap<String, Pair<Long, String>> compatPkgs = getCompatibilityPackagesLocked();
+        if (compatPkgs != null) {
+            pw.print(prefix); pw.print("Compat pkgs: "); pw.println(compatPkgs.keySet());
+        }
         pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
         pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
 
@@ -1030,23 +1034,11 @@
     }
 
     @GuardedBy("mLock")
-    boolean isCompatibilityModeRequestedLocked(@NonNull String packageName,
-            long versionCode) {
-        if (mInfo == null || !mInfo.isCompatibilityModeRequested(packageName, versionCode)) {
-            return false;
+    @Nullable ArrayMap<String, Pair<Long, String>> getCompatibilityPackagesLocked() {
+        if (mInfo != null) {
+            return mInfo.getCompatibilityPackages();
         }
-        if (!Build.IS_ENG) {
-            // TODO: Build a map and watch for settings changes (this is called on app start)
-            final String whiteListedPackages = getWhitelistedCompatModePackages();
-            return whiteListedPackages != null && whiteListedPackages.contains(packageName);
-        }
-        return true;
-    }
-
-    private String getWhitelistedCompatModePackages() {
-        return Settings.Global.getString(
-                mContext.getContentResolver(),
-                Settings.Global.AUTOFILL_COMPAT_ALLOWED_PACKAGES);
+        return null;
     }
 
     private void sendStateToClients(boolean resetClient) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 55c36b0..d047581 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1174,12 +1174,12 @@
             @NonNull UserData userData, @NonNull Collection<ViewState> viewStates) {
 
         final String[] userValues = userData.getValues();
-        final String[] remoteIds = userData.getRemoteIds();
+        final String[] categoryIds = userData.getCategoryIds();
 
         // Sanity check
-        if (userValues == null || remoteIds == null || userValues.length != remoteIds.length) {
+        if (userValues == null || categoryIds == null || userValues.length != categoryIds.length) {
             final int valuesLength = userValues == null ? -1 : userValues.length;
-            final int idsLength = remoteIds == null ? -1 : remoteIds.length;
+            final int idsLength = categoryIds == null ? -1 : categoryIds.length;
             Slog.w(TAG, "setScores(): user data mismatch: values.length = "
                     + valuesLength + ", ids.length = " + idsLength);
             return;
@@ -1196,12 +1196,12 @@
         final int viewsSize = viewStates.size();
 
         // First, we get all scores.
-        final AutofillId[] fieldIds = new AutofillId[viewsSize];
+        final AutofillId[] autofillIds = new AutofillId[viewsSize];
         final ArrayList<AutofillValue> currentValues = new ArrayList<>(viewsSize);
         int k = 0;
         for (ViewState viewState : viewStates) {
             currentValues.add(viewState.getCurrentValue());
-            fieldIds[k++] = viewState.id;
+            autofillIds[k++] = viewState.id;
         }
 
         // Then use the results, asynchronously
@@ -1221,32 +1221,53 @@
             }
             int i = 0, j = 0;
             try {
+                // Iteract over all autofill fields first
                 for (i = 0; i < viewsSize; i++) {
-                    final AutofillId fieldId = fieldIds[i];
+                    final AutofillId autofillId = autofillIds[i];
 
-                    ArrayList<Match> matches = null;
+                    // Search the best scores for each category (as some categories could have
+                    // multiple user values
+                    ArrayMap<String, Float> scoresByField = null;
                     for (j = 0; j < userValues.length; j++) {
-                        String remoteId = remoteIds[j];
+                        final String categoryId = categoryIds[j];
                         final float score = scores.scores[i][j];
                         if (score > 0) {
+                            if (scoresByField == null) {
+                                scoresByField = new ArrayMap<>(userValues.length);
+                            }
+                            final Float currentScore = scoresByField.get(categoryId);
+                            if (currentScore != null && currentScore > score) {
+                                if (sVerbose) {
+                                    Slog.v(TAG,  "skipping score " + score
+                                            + " because it's less than " + currentScore);
+                                }
+                                continue;
+                            }
                             if (sVerbose) {
                                 Slog.v(TAG, "adding score " + score + " at index " + j + " and id "
-                                        + fieldId);
+                                        + autofillId);
                             }
-                            if (matches == null) {
-                                matches = new ArrayList<>(userValues.length);
-                            }
-                            matches.add(new Match(remoteId, score));
+                            scoresByField.put(categoryId, score);
                         }
                         else if (sVerbose) {
-                            Slog.v(TAG, "skipping score 0 at index " + j + " and id " + fieldId);
+                            Slog.v(TAG, "skipping score 0 at index " + j + " and id " + autofillId);
                         }
                     }
-                    if (matches != null) {
-                        detectedFieldIds.add(fieldId);
-                        detectedFieldClassifications.add(new FieldClassification(matches));
+                    if (scoresByField == null) {
+                        if (sVerbose) Slog.v(TAG, "no score for autofillId=" + autofillId);
+                        continue;
                     }
-                }
+
+                    // Then create the matches for that autofill id
+                    final ArrayList<Match> matches = new ArrayList<>(scoresByField.size());
+                    for (j = 0; j < scoresByField.size(); j++) {
+                        final String fieldId = scoresByField.keyAt(j);
+                        final float score = scoresByField.valueAt(j);
+                        matches.add(new Match(fieldId, score));
+                    }
+                    detectedFieldIds.add(autofillId);
+                    detectedFieldClassifications.add(new FieldClassification(matches));
+                } // for i
             } catch (ArrayIndexOutOfBoundsException e) {
                 wtf(e, "Error accessing FC score at [%d, %d] (%s): %s", i, j, scores, e);
                 return;
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
new file mode 100644
index 0000000..1c9a43a
--- /dev/null
+++ b/services/backup/OWNERS
@@ -0,0 +1,7 @@
+artikz@google.com
+brufino@google.com
+bryanmawhinney@google.com
+ctate@google.com
+jorlow@google.com
+mkarpinski@google.com
+
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index 7e179e5..501ff29 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -26,6 +26,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.ArrayMap;
@@ -57,6 +58,8 @@
     @VisibleForTesting
     public static final String SERVICE_ACTION_TRANSPORT_HOST = "android.backup.TRANSPORT_HOST";
 
+    private static final String EXTRA_TRANSPORT_REGISTRATION = "transport_registration";
+
     private final Intent mTransportServiceIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST);
     private final Context mContext;
     private final PackageManager mPackageManager;
@@ -582,8 +585,12 @@
 
         String transportString = transportComponent.flattenToShortString();
         String callerLogString = "TransportManager.registerTransport()";
-        TransportClient transportClient =
-                mTransportClientManager.getTransportClient(transportComponent, callerLogString);
+
+        Bundle extras = new Bundle();
+        extras.putBoolean(EXTRA_TRANSPORT_REGISTRATION, true);
+
+        TransportClient transportClient = mTransportClientManager.getTransportClient(
+            transportComponent, extras, callerLogString);
         final IBackupTransport transport;
         try {
             transport = transportClient.connectOrThrow(callerLogString);
diff --git a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
index 4041932..96e7d2f 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClientManager.java
@@ -22,10 +22,9 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-
+import android.os.Bundle;
 import com.android.server.backup.TransportManager;
 import com.android.server.backup.transport.TransportUtils.Priority;
-
 import java.io.PrintWriter;
 import java.util.Map;
 import java.util.WeakHashMap;
@@ -59,6 +58,32 @@
     public TransportClient getTransportClient(ComponentName transportComponent, String caller) {
         Intent bindIntent =
                 new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(transportComponent);
+
+        return getTransportClient(transportComponent, caller, bindIntent);
+    }
+
+    /**
+     * Retrieves a {@link TransportClient} for the transport identified by {@param
+     * transportComponent} whose binding intent will have the {@param extras} extras.
+     *
+     * @param transportComponent The {@link ComponentName} of the transport.
+     * @param extras A {@link Bundle} of extras to pass to the binding intent.
+     * @param caller A {@link String} identifying the caller for logging/debugging purposes. Check
+     *     {@link TransportClient#connectAsync(TransportConnectionListener, String)} for more
+     *     details.
+     * @return A {@link TransportClient}.
+     */
+    public TransportClient getTransportClient(
+            ComponentName transportComponent, Bundle extras, String caller) {
+        Intent bindIntent =
+                new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(transportComponent);
+        bindIntent.putExtras(extras);
+
+        return getTransportClient(transportComponent, caller, bindIntent);
+    }
+
+    private TransportClient getTransportClient(
+            ComponentName transportComponent, String caller, Intent bindIntent) {
         synchronized (mTransportClientsLock) {
             TransportClient transportClient =
                     new TransportClient(
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index efad7d5..f633003 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -30,6 +30,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
@@ -1290,11 +1291,7 @@
                         for (Network network : networks) {
                             nai = getNetworkAgentInfoForNetwork(network);
                             nc = getNetworkCapabilitiesInternal(nai);
-                            // nc is a copy of the capabilities in nai, so it's fine to mutate it
-                            // TODO : don't remove the UIDs when communicating with processes
-                            // that have the NETWORK_SETTINGS permission.
                             if (nc != null) {
-                                nc.setSingleUid(userId);
                                 result.put(network, nc);
                             }
                         }
@@ -1362,7 +1359,9 @@
         if (nai != null) {
             synchronized (nai) {
                 if (nai.networkCapabilities != null) {
-                    return new NetworkCapabilities(nai.networkCapabilities);
+                    // TODO : don't remove the UIDs when communicating with processes
+                    // that have the NETWORK_SETTINGS permission.
+                    return networkCapabilitiesWithoutUids(nai.networkCapabilities);
                 }
             }
         }
@@ -1375,6 +1374,10 @@
         return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
     }
 
+    private NetworkCapabilities networkCapabilitiesWithoutUids(NetworkCapabilities nc) {
+        return new NetworkCapabilities(nc).setUids(null);
+    }
+
     @Override
     public NetworkState[] getAllNetworkState() {
         // Require internal since we're handing out IMSI details
@@ -1384,6 +1387,10 @@
         for (Network network : getAllNetworks()) {
             final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
             if (nai != null) {
+                // TODO (b/73321673) : NetworkState contains a copy of the
+                // NetworkCapabilities, which may contain UIDs of apps to which the
+                // network applies. Should the UIDs be cleared so as not to leak or
+                // interfere ?
                 result.add(nai.getNetworkState());
             }
         }
@@ -4542,10 +4549,12 @@
         lp.ensureDirectlyConnectedRoutes();
         // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
         // satisfies mDefaultRequest.
+        final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
         final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
-                new Network(reserveNetId()), new NetworkInfo(networkInfo), lp,
-                new NetworkCapabilities(networkCapabilities), currentScore,
+                new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore,
                 mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this);
+        // Make sure the network capabilities reflect what the agent info says.
+        nai.networkCapabilities = mixInCapabilities(nai, nc);
         synchronized (this) {
             nai.networkMonitor.systemReady = mSystemReady;
         }
@@ -4774,6 +4783,11 @@
         } else {
             newNc.addCapability(NET_CAPABILITY_FOREGROUND);
         }
+        if (nai.isSuspended()) {
+            newNc.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        } else {
+            newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+        }
 
         return newNc;
     }
@@ -4954,7 +4968,7 @@
         releasePendingNetworkRequestWithDelay(pendingIntent);
     }
 
-    private static void callCallbackForRequest(NetworkRequestInfo nri,
+    private void callCallbackForRequest(NetworkRequestInfo nri,
             NetworkAgentInfo networkAgent, int notificationType, int arg1) {
         if (nri.messenger == null) {
             return;  // Default request has no msgr
@@ -4967,16 +4981,19 @@
             putParcelable(bundle, networkAgent.network);
         }
         switch (notificationType) {
+            case ConnectivityManager.CALLBACK_AVAILABLE: {
+                putParcelable(bundle, new NetworkCapabilities(networkAgent.networkCapabilities));
+                putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
+                break;
+            }
             case ConnectivityManager.CALLBACK_LOSING: {
                 msg.arg1 = arg1;
                 break;
             }
             case ConnectivityManager.CALLBACK_CAP_CHANGED: {
+                // networkAgent can't be null as it has been accessed a few lines above.
                 final NetworkCapabilities nc =
-                        new NetworkCapabilities(networkAgent.networkCapabilities);
-                // TODO : don't remove the UIDs when communicating with processes
-                // that have the NETWORK_SETTINGS permission.
-                nc.setSingleUid(nri.mUid);
+                        networkCapabilitiesWithoutUids(networkAgent.networkCapabilities);
                 putParcelable(bundle, nc);
                 break;
             }
@@ -5509,6 +5526,10 @@
             if (networkAgent.getCurrentScore() != oldScore) {
                 rematchAllNetworksAndRequests(networkAgent, oldScore);
             }
+            updateCapabilities(networkAgent.getCurrentScore(), networkAgent,
+                    networkAgent.networkCapabilities);
+            // TODO (b/73132094) : remove this call once the few users of onSuspended and
+            // onResumed have been removed.
             notifyNetworkCallbacks(networkAgent, (state == NetworkInfo.State.SUSPENDED ?
                     ConnectivityManager.CALLBACK_SUSPENDED :
                     ConnectivityManager.CALLBACK_RESUMED));
@@ -5545,14 +5566,6 @@
         }
 
         callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, 0);
-        // Whether a network is currently suspended is also an important
-        // element of state to be transferred (it would not otherwise be
-        // delivered by any currently available mechanism).
-        if (nai.networkInfo.getState() == NetworkInfo.State.SUSPENDED) {
-            callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_SUSPENDED, 0);
-        }
-        callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_CAP_CHANGED, 0);
-        callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_IP_CHANGED, 0);
     }
 
     private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index bdeb231..7cd007b 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -120,7 +120,6 @@
 import android.service.vr.IVrStateCallbacks;
 import android.text.TextUtils;
 import android.text.style.SuggestionSpan;
-import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.EventLog;
@@ -183,7 +182,6 @@
 public class InputMethodManagerService extends IInputMethodManager.Stub
         implements ServiceConnection, Handler.Callback {
     static final boolean DEBUG = false;
-    static final boolean DEBUG_RESTORE = DEBUG || false;
     static final String TAG = "InputMethodManagerService";
 
     @Retention(SOURCE)
@@ -911,15 +909,6 @@
                     || Intent.ACTION_USER_REMOVED.equals(action)) {
                 updateCurrentProfileIds();
                 return;
-            } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
-                final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
-                if (Settings.Secure.ENABLED_INPUT_METHODS.equals(name)) {
-                    final String prevValue = intent.getStringExtra(
-                            Intent.EXTRA_SETTING_PREVIOUS_VALUE);
-                    final String newValue = intent.getStringExtra(
-                            Intent.EXTRA_SETTING_NEW_VALUE);
-                    restoreEnabledInputMethods(mContext, prevValue, newValue);
-                }
             } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
                 onActionLocaleChanged();
             } else if (ACTION_SHOW_INPUT_METHOD_PICKER.equals(action)) {
@@ -984,44 +973,6 @@
         }
     }
 
-    // Apply the results of a restore operation to the set of enabled IMEs.  Note that this
-    // does not attempt to validate on the fly with any installed device policy, so must only
-    // be run in the context of initial device setup.
-    //
-    // TODO: Move this method to InputMethodUtils with adding unit tests.
-    static void restoreEnabledInputMethods(Context context, String prevValue, String newValue) {
-        if (DEBUG_RESTORE) {
-            Slog.i(TAG, "Restoring enabled input methods:");
-            Slog.i(TAG, "prev=" + prevValue);
-            Slog.i(TAG, " new=" + newValue);
-        }
-        // 'new' is the just-restored state, 'prev' is what was in settings prior to the restore
-        ArrayMap<String, ArraySet<String>> prevMap =
-                InputMethodUtils.parseInputMethodsAndSubtypesString(prevValue);
-        ArrayMap<String, ArraySet<String>> newMap =
-                InputMethodUtils.parseInputMethodsAndSubtypesString(newValue);
-
-        // Merge the restored ime+subtype enabled states into the live state
-        for (ArrayMap.Entry<String, ArraySet<String>> entry : newMap.entrySet()) {
-            final String imeId = entry.getKey();
-            ArraySet<String> prevSubtypes = prevMap.get(imeId);
-            if (prevSubtypes == null) {
-                prevSubtypes = new ArraySet<>(2);
-                prevMap.put(imeId, prevSubtypes);
-            }
-            prevSubtypes.addAll(entry.getValue());
-        }
-
-        final String mergedImesAndSubtypesString =
-                InputMethodUtils.buildInputMethodsAndSubtypesString(prevMap);
-        if (DEBUG_RESTORE) {
-            Slog.i(TAG, "Merged IME string:");
-            Slog.i(TAG, "     " + mergedImesAndSubtypesString);
-        }
-        Settings.Secure.putString(context.getContentResolver(),
-                Settings.Secure.ENABLED_INPUT_METHODS, mergedImesAndSubtypesString);
-    }
-
     final class MyPackageMonitor extends PackageMonitor {
         /**
          * Package names that are known to contain {@link InputMethodService}.
@@ -1577,7 +1528,6 @@
                 broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
                 broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
                 broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
-                broadcastFilter.addAction(Intent.ACTION_SETTING_RESTORED);
                 broadcastFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
                 broadcastFilter.addAction(ACTION_SHOW_INPUT_METHOD_PICKER);
                 mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter);
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 87e8121..8367916 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -81,7 +81,7 @@
     static final boolean DEBUG_FOREGROUND_SERVICE = DEBUG_ALL || false;
     static final boolean DEBUG_SERVICE_EXECUTING = DEBUG_ALL || false;
     static final boolean DEBUG_STACK = DEBUG_ALL || false;
-    static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false;
+    static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || true;
     static final boolean DEBUG_SWITCH = DEBUG_ALL || false;
     static final boolean DEBUG_TASKS = DEBUG_ALL || false;
     static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e8b7839..5f8a5ab 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -380,6 +380,7 @@
 import android.view.Gravity;
 import android.view.IRecentsAnimationRunner;
 import android.view.LayoutInflater;
+import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
 import android.view.View;
 import android.view.WindowManager;
@@ -579,6 +580,10 @@
     // How long we wait until we timeout on key dispatching during instrumentation.
     static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT = 60*1000;
 
+    // Disable hidden API checks for the newly started instrumentation.
+    // Must be kept in sync with Am.
+    private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
+
     // How long to wait in getAssistContextExtras for the activity and foreground services
     // to respond with the result.
     static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
@@ -4002,12 +4007,19 @@
         startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */);
     }
 
+    @GuardedBy("this")
+    private final boolean startProcessLocked(ProcessRecord app,
+            String hostingType, String hostingNameStr, String abiOverride) {
+        return startProcessLocked(app, hostingType, hostingNameStr,
+                false /* disableHiddenApiChecks */, abiOverride);
+    }
+
     /**
      * @return {@code true} if process start is successful, false otherwise.
      */
     @GuardedBy("this")
     private final boolean startProcessLocked(ProcessRecord app, String hostingType,
-            String hostingNameStr, String abiOverride) {
+            String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride) {
         if (app.pendingStart) {
             return true;
         }
@@ -4130,7 +4142,9 @@
                 runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
             }
 
-            if (!app.info.isAllowedToUseHiddenApi() && !mHiddenApiBlacklist.isDisabled()) {
+            if (!app.info.isAllowedToUseHiddenApi() &&
+                    !disableHiddenApiChecks &&
+                    !mHiddenApiBlacklist.isDisabled()) {
                 // This app is not allowed to use undocumented and private APIs, or blacklisting is
                 // enabled. Set up its runtime with the appropriate flag.
                 runtimeFlags |= Zygote.ENABLE_HIDDEN_API_CHECKS;
@@ -10099,6 +10113,75 @@
     }
 
     /**
+     * Updates (grants or revokes) a persitable URI permission.
+     *
+     * @param uri URI to be granted or revoked.
+     * @param prefix if {@code false}, permission apply to this specific URI; if {@code true}, it
+     * applies to all URIs that are prefixed by this URI.
+     * @param packageName target package.
+     * @param grant if {@code true} a new permission will be granted, otherwise an existing
+     * permission will be revoked.
+     * @param userId user handle
+     *
+     * @return whether or not the requested succeeded.
+     *
+     * @deprecated TODO(b/72055774): caller should use takePersistableUriPermission() or
+     * releasePersistableUriPermission() instead, but such change will be made in a separate CL
+     * so it can be easily reverted if it breaks existing functionality.
+     */
+    @Deprecated // STOPSHIP if not removed
+    @Override
+    public boolean updatePersistableUriPermission(Uri uri, boolean prefix, String packageName,
+            boolean grant, int userId) {
+        enforceCallingPermission(android.Manifest.permission.GET_APP_GRANTED_URI_PERMISSIONS,
+                "updatePersistableUriPermission");
+        final int uid = mPackageManagerInt.getPackageUid(packageName, 0, userId);
+
+        final GrantUri grantUri = new GrantUri(userId, uri, prefix);
+
+        boolean persistChanged = false;
+        synchronized (this) {
+            if (grant) { // Grant
+                final String authority = uri.getAuthority();
+                final ProviderInfo pi = getProviderInfoLocked(authority, userId, 0);
+                if (pi == null) {
+                    Slog.w(TAG, "No content provider found for authority " + authority);
+                    return false;
+                }
+                final UriPermission permission = findOrCreateUriPermissionLocked(pi.packageName,
+                        packageName, uid, grantUri);
+                if (permission.isNew()) {
+                    final int modeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION
+                            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
+                    permission.initPersistedModes(modeFlags, System.currentTimeMillis());
+                    persistChanged = true;
+                } else {
+                    // Caller should not try to grant permission that is already granted.
+                    Slog.w(TAG_URI_PERMISSION,
+                            "permission already granted for " + grantUri.toSafeString());
+                    return false;
+                }
+                persistChanged |= maybePrunePersistedUriGrantsLocked(uid);
+            } else { // Revoke
+                final UriPermission permission = findUriPermissionLocked(uid, grantUri);
+                if (permission == null) {
+                    // Caller should not try to revoke permission that is not granted.
+                    Slog.v(TAG_URI_PERMISSION, "no permission for " + grantUri.toSafeString());
+                    return false;
+                } else {
+                    permission.modeFlags = 0;
+                    removeUriPermissionIfNeededLocked(permission);
+                    persistChanged = true;
+                }
+            }
+            if (persistChanged) {
+                schedulePersistUriGrants();
+            }
+        }
+        return true;
+    }
+
+    /**
      * @param uri This uri must NOT contain an embedded userId.
      * @param userId The userId in which the uri is to be resolved.
      */
@@ -11362,9 +11445,6 @@
             throw new IllegalArgumentException("Invalid task, not in foreground");
         }
 
-        // When a task is locked, dismiss the pinned stack if it exists
-        mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
-
         // {@code isSystemCaller} is used to distinguish whether this request is initiated by the
         // system or a specific app.
         // * System-initiated requests will only start the pinned mode (screen pinning)
@@ -11374,6 +11454,9 @@
         final int callingUid = Binder.getCallingUid();
         long ident = Binder.clearCallingIdentity();
         try {
+            // When a task is locked, dismiss the pinned stack if it exists
+            mStackSupervisor.removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
+
             mLockTaskController.startLockTaskMode(task, isSystemCaller, callingUid);
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -12782,6 +12865,12 @@
     @GuardedBy("this")
     final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
             String abiOverride) {
+        return addAppLocked(info, customProcess, isolated, false /* disableHiddenApiChecks */,
+                abiOverride);
+    }
+
+    final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
+            boolean disableHiddenApiChecks, String abiOverride) {
         ProcessRecord app;
         if (!isolated) {
             app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
@@ -12813,7 +12902,8 @@
         if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
             mPersistentStartingProcesses.add(app);
             startProcessLocked(app, "added application",
-                    customProcess != null ? customProcess : app.processName, abiOverride);
+                    customProcess != null ? customProcess : app.processName, disableHiddenApiChecks,
+                    abiOverride);
         }
 
         return app;
@@ -21624,7 +21714,10 @@
                 mUsageStatsService.reportEvent(ii.targetPackage, userId,
                         UsageEvents.Event.SYSTEM_INTERACTION);
             }
-            ProcessRecord app = addAppLocked(ai, defProcess, false, abiOverride);
+            boolean disableHiddenApiChecks =
+                    (flags & INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0;
+            ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
+                    abiOverride);
             app.instr = activeInstr;
             activeInstr.mFinished = false;
             activeInstr.mRunningProcesses.add(app);
@@ -26336,4 +26429,20 @@
             }
         }
     }
+
+    @Override
+    public void registerRemoteAnimationForNextActivityStart(String packageName,
+            RemoteAnimationAdapter adapter) throws RemoteException {
+        enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
+                "registerRemoteAnimationForNextActivityStart");
+        synchronized (this) {
+            final long origId = Binder.clearCallingIdentity();
+            try {
+                mActivityStartController.registerRemoteAnimationForNextActivityStart(packageName,
+                        adapter);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 24a77c7..fa0df56 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -2608,7 +2608,7 @@
             pw.println("          specified then send to all users.");
             pw.println("      --receiver-permission <PERMISSION>: Require receiver to hold permission.");
             pw.println("  instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
-            pw.println("          [--user <USER_ID> | current]");
+            pw.println("          [--user <USER_ID> | current] [--no-hidden-api-checks]");
             pw.println("          [--no-window-animation] [--abi <ABI>] <COMPONENT>");
             pw.println("      Start an Instrumentation.  Typically this target <COMPONENT> is in the");
             pw.println("      form <TEST_PACKAGE>/<RUNNER_CLASS> or only <TEST_PACKAGE> if there");
@@ -2626,6 +2626,7 @@
             pw.println("          test runners.");
             pw.println("      --user <USER_ID> | current: Specify user instrumentation runs in;");
             pw.println("          current user if not specified.");
+            pw.println("      --no-hidden-api-checks: disable restrictions on use of hidden API.");
             pw.println("      --no-window-animation: turn off window animations while running.");
             pw.println("      --abi <ABI>: Launch the instrumented process with the selected ABI.");
             pw.println("          This assumes that the process supports the selected ABI.");
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 9838851..ddba349 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -380,8 +380,9 @@
     }
 
     String getLifecycleDescription(String reason) {
-        return "component:" + intent.getComponent().flattenToShortString() + ", state=" + state
-                + ", reason=" + reason + ", time=" + System.currentTimeMillis();
+        return "name= " + this + ", component=" + intent.getComponent().flattenToShortString()
+                + ", package=" + packageName + ", state=" + state + ", reason=" + reason + ", time="
+                + System.currentTimeMillis();
     }
 
     void dump(PrintWriter pw, String prefix) {
@@ -2583,7 +2584,8 @@
             if (andResume) {
                 lifecycleItem = ResumeActivityItem.obtain(service.isNextTransitionForward());
             } else {
-                lifecycleItem = PauseActivityItem.obtain();
+                lifecycleItem = PauseActivityItem.obtain()
+                        .setDescription(getLifecycleDescription("relaunchActivityLocked"));
             }
             final ClientTransaction transaction = ClientTransaction.obtain(app.thread, appToken);
             transaction.addCallback(callbackItem);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 055a1aa..812de88 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1444,7 +1444,8 @@
 
                 mService.mLifecycleManager.scheduleTransaction(prev.app.thread, prev.appToken,
                         PauseActivityItem.obtain(prev.finishing, userLeaving,
-                                prev.configChangeFlags, pauseImmediately));
+                                prev.configChangeFlags, pauseImmediately).setDescription(
+                                        prev.getLifecycleDescription("startPausingLocked")));
             } catch (Exception e) {
                 // Ignore exception, if process died other code will cleanup.
                 Slog.w(TAG, "Exception thrown during pause", e);
@@ -1524,7 +1525,8 @@
                     if (r.finishing) {
                         if (DEBUG_PAUSE) Slog.v(TAG,
                                 "Executing finish of failed to pause activity: " + r);
-                        finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false);
+                        finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false,
+                                "activityPausedLocked");
                     }
                 }
             }
@@ -1541,7 +1543,8 @@
             prev.state = ActivityState.PAUSED;
             if (prev.finishing) {
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
-                prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false);
+                prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false,
+                        "completedPausedLocked");
             } else if (prev.app != null) {
                 if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
                         + " wasStopping=" + wasStopping + " visible=" + prev.visible);
@@ -3673,8 +3676,8 @@
 
                 final int finishMode = (r.visible || r.nowVisible) ? FINISH_AFTER_VISIBLE
                         : FINISH_AFTER_PAUSE;
-                final boolean removedActivity = finishCurrentActivityLocked(r, finishMode, oomAdj)
-                        == null;
+                final boolean removedActivity = finishCurrentActivityLocked(r, finishMode, oomAdj,
+                        "finishActivityLocked") == null;
 
                 // The following code is an optimization. When the last non-task overlay activity
                 // is removed from the task, we remove the entire task from the stack. However,
@@ -3715,7 +3718,8 @@
     static final int FINISH_AFTER_PAUSE = 1;
     static final int FINISH_AFTER_VISIBLE = 2;
 
-    final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj) {
+    final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj,
+            String reason) {
         // First things first: if this activity is currently visible,
         // and the resumed activity is not yet visible, then hold off on
         // finishing until the resumed one becomes visible.
@@ -3758,7 +3762,7 @@
                 || prevState == STOPPED
                 || prevState == ActivityState.INITIALIZING) {
             r.makeFinishingLocked();
-            boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm");
+            boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);
 
             if (finishingActivityInNonFocusedStack) {
                 // Finishing activity that was in paused state and it was in not currently focused
@@ -3794,7 +3798,8 @@
                     continue;
                 }
                 Slog.d(TAG, "finishAllActivitiesLocked: finishing " + r + " immediately");
-                finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
+                finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false,
+                        "finishAllActivitiesLocked");
             }
         }
         if (noActivitiesInStack) {
@@ -4882,7 +4887,8 @@
                             + r.intent.getComponent().flattenToShortString());
                     // Force the destroy to skip right to removal.
                     r.app = null;
-                    finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);
+                    finishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false,
+                            "handleAppCrashedLocked");
                 }
             }
         }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 4928e90..5c30764 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1453,7 +1453,8 @@
                     lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward())
                             .setDescription(r.getLifecycleDescription("realStartActivityLocked"));
                 } else {
-                    lifecycleItem = PauseActivityItem.obtain();
+                    lifecycleItem = PauseActivityItem.obtain()
+                            .setDescription(r.getLifecycleDescription("realStartActivityLocked"));
                 }
                 clientTransaction.setLifecycleStateRequest(lifecycleItem);
 
@@ -1955,7 +1956,8 @@
             final ActivityStack stack = r.getStack();
             if (stack != null) {
                 if (r.finishing) {
-                    stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false);
+                    stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
+                            "activityIdleInternalLocked");
                 } else {
                     stack.stopActivityLocked(r);
                 }
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index da11f68..868f90d 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -41,6 +41,7 @@
 import android.os.Message;
 import android.provider.Settings;
 import android.util.Slog;
+import android.view.RemoteAnimationAdapter;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.am.ActivityStackSupervisor.PendingActivityLaunch;
@@ -85,6 +86,8 @@
 
     private final Handler mHandler;
 
+    private final PendingRemoteAnimationRegistry mPendingRemoteAnimationRegistry;
+
     private final class StartHandler extends Handler {
         public StartHandler(Looper looper) {
             super(looper, null, true);
@@ -123,6 +126,8 @@
         mHandler = new StartHandler(mService.mHandlerThread.getLooper());
         mFactory = factory;
         mFactory.setController(this);
+        mPendingRemoteAnimationRegistry = new PendingRemoteAnimationRegistry(service,
+                service.mHandler);
     }
 
     /**
@@ -399,6 +404,15 @@
         return mPendingActivityLaunches.size() < pendingLaunches;
     }
 
+    void registerRemoteAnimationForNextActivityStart(String packageName,
+            RemoteAnimationAdapter adapter) {
+        mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter);
+    }
+
+    PendingRemoteAnimationRegistry getPendingRemoteAnimationRegistry() {
+        return mPendingRemoteAnimationRegistry;
+    }
+
     void dump(PrintWriter pw, String prefix, String dumpPackage) {
         pw.print(prefix);
         pw.print("mLastHomeActivityStartResult=");
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 055b89b..8205265 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -708,6 +708,8 @@
         ActivityOptions checkedOptions = options != null
                 ? options.getOptions(intent, aInfo, callerApp, mSupervisor)
                 : null;
+        checkedOptions = mService.getActivityStartController().getPendingRemoteAnimationRegistry()
+                .overrideOptionsIfNeeded(callingPackage, checkedOptions);
         if (mService.mController != null) {
             try {
                 // The Intent we give to the watcher has the extra data
@@ -930,6 +932,7 @@
         // Don't modify the client's object!
         intent = new Intent(intent);
         if (componentSpecified
+                && !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null)
                 && !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
                 && !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
                 && mService.getPackageManagerInternalLocked()
diff --git a/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java b/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java
new file mode 100644
index 0000000..77713f5
--- /dev/null
+++ b/services/core/java/com/android/server/am/PendingRemoteAnimationRegistry.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 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 com.android.server.am;
+
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.os.Handler;
+import android.util.ArrayMap;
+import android.view.RemoteAnimationAdapter;
+
+/**
+ * Registry to keep track of remote animations to be run for activity starts from a certain package.
+ *
+ * @see ActivityManagerService#registerRemoteAnimationForNextActivityStart
+ */
+class PendingRemoteAnimationRegistry {
+
+    private static final long TIMEOUT_MS = 3000;
+
+    private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();
+    private final Handler mHandler;
+    private final ActivityManagerService mService;
+
+    PendingRemoteAnimationRegistry(ActivityManagerService service, Handler handler) {
+        mService = service;
+        mHandler = handler;
+    }
+
+    /**
+     * Adds a remote animation to be run for all activity starts originating from a certain package.
+     */
+    void addPendingAnimation(String packageName, RemoteAnimationAdapter adapter) {
+        mEntries.put(packageName, new Entry(packageName, adapter));
+    }
+
+    /**
+     * Overrides the activity options with a registered remote animation for a certain calling
+     * package if such a remote animation is registered.
+     */
+    ActivityOptions overrideOptionsIfNeeded(String callingPackage,
+            @Nullable ActivityOptions options) {
+        final Entry entry = mEntries.get(callingPackage);
+        if (entry == null) {
+            return options;
+        }
+        if (options == null) {
+            options = ActivityOptions.makeRemoteAnimation(entry.adapter);
+        } else {
+            options.setRemoteAnimationAdapter(entry.adapter);
+        }
+        mEntries.remove(callingPackage);
+        return options;
+    }
+
+    private class Entry {
+        final String packageName;
+        final RemoteAnimationAdapter adapter;
+
+        Entry(String packageName, RemoteAnimationAdapter adapter) {
+            this.packageName = packageName;
+            this.adapter = adapter;
+            mHandler.postDelayed(() -> {
+                synchronized (mService) {
+                    final Entry entry = mEntries.get(packageName);
+                    if (entry == this) {
+                        mEntries.remove(packageName);
+                    }
+                }
+            }, TIMEOUT_MS);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/UriPermission.java b/services/core/java/com/android/server/am/UriPermission.java
index 1e071aa..3bf1cf4 100644
--- a/services/core/java/com/android/server/am/UriPermission.java
+++ b/services/core/java/com/android/server/am/UriPermission.java
@@ -124,6 +124,10 @@
         updateModeFlags();
     }
 
+    boolean isNew() {
+        return persistedCreateTime == INVALID_TIME;
+    }
+
     void grantModes(int modeFlags, UriPermissionOwner owner) {
         final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0;
         modeFlags &= (Intent.FLAG_GRANT_READ_URI_PERMISSION
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c5424b7..76e0d89 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1028,14 +1028,16 @@
     }
 
     private void checkAllAliasStreamVolumes() {
-        synchronized (VolumeStreamState.class) {
-            int numStreamTypes = AudioSystem.getNumStreamTypes();
-            for (int streamType = 0; streamType < numStreamTypes; streamType++) {
-                mStreamStates[streamType]
-                        .setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], TAG);
-                // apply stream volume
-                if (!mStreamStates[streamType].mIsMuted) {
-                    mStreamStates[streamType].applyAllVolumes();
+        synchronized (mSettingsLock) {
+            synchronized (VolumeStreamState.class) {
+                int numStreamTypes = AudioSystem.getNumStreamTypes();
+                for (int streamType = 0; streamType < numStreamTypes; streamType++) {
+                    mStreamStates[streamType]
+                            .setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], TAG);
+                    // apply stream volume
+                    if (!mStreamStates[streamType].mIsMuted) {
+                        mStreamStates[streamType].applyAllVolumes();
+                    }
                 }
             }
         }
@@ -1141,13 +1143,16 @@
         if (updateVolumes && mStreamStates != null) {
             updateDefaultVolumes();
 
-            mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
-                    caller);
-
-            mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName =
-                    System.VOLUME_SETTINGS_INT[a11yStreamAlias];
-            mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes(
-                    mStreamStates[a11yStreamAlias], caller);
+            synchronized (mSettingsLock) {
+                synchronized (VolumeStreamState.class) {
+                    mStreamStates[AudioSystem.STREAM_DTMF]
+                            .setAllIndexes(mStreamStates[dtmfStreamAlias], caller);
+                    mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName =
+                            System.VOLUME_SETTINGS_INT[a11yStreamAlias];
+                    mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes(
+                            mStreamStates[a11yStreamAlias], caller);
+                }
+            }
             if (sIndependentA11yVolume) {
                 // restore the a11y values from the settings
                 mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].readSettings();
@@ -4590,39 +4595,36 @@
          * @param srcStream
          * @param caller
          */
+        // must be sync'd on mSettingsLock before VolumeStreamState.class
+        @GuardedBy("VolumeStreamState.class")
         public void setAllIndexes(VolumeStreamState srcStream, String caller) {
             if (mStreamType == srcStream.mStreamType) {
                 return;
             }
-            synchronized (mSettingsLock) {
-                synchronized (VolumeStreamState.class) {
-                    int srcStreamType = srcStream.getStreamType();
-                    // apply default device volume from source stream to all devices first in case
-                    // some devices are present in this stream state but not in source stream state
-                    int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
-                    index = rescaleIndex(index, srcStreamType, mStreamType);
-                    for (int i = 0; i < mIndexMap.size(); i++) {
-                        mIndexMap.put(mIndexMap.keyAt(i), index);
-                    }
-                    // Now apply actual volume for devices in source stream state
-                    SparseIntArray srcMap = srcStream.mIndexMap;
-                    for (int i = 0; i < srcMap.size(); i++) {
-                        int device = srcMap.keyAt(i);
-                        index = srcMap.valueAt(i);
-                        index = rescaleIndex(index, srcStreamType, mStreamType);
+            int srcStreamType = srcStream.getStreamType();
+            // apply default device volume from source stream to all devices first in case
+            // some devices are present in this stream state but not in source stream state
+            int index = srcStream.getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
+            index = rescaleIndex(index, srcStreamType, mStreamType);
+            for (int i = 0; i < mIndexMap.size(); i++) {
+                mIndexMap.put(mIndexMap.keyAt(i), index);
+            }
+            // Now apply actual volume for devices in source stream state
+            SparseIntArray srcMap = srcStream.mIndexMap;
+            for (int i = 0; i < srcMap.size(); i++) {
+                int device = srcMap.keyAt(i);
+                index = srcMap.valueAt(i);
+                index = rescaleIndex(index, srcStreamType, mStreamType);
 
-                        setIndex(index, device, caller);
-                    }
-                }
+                setIndex(index, device, caller);
             }
         }
 
-        @GuardedBy("mSettingsLock")
+        // must be sync'd on mSettingsLock before VolumeStreamState.class
+        @GuardedBy("VolumeStreamState.class")
         public void setAllIndexesToMax() {
-            synchronized (VolumeStreamState.class) {
-                for (int i = 0; i < mIndexMap.size(); i++) {
-                    mIndexMap.put(mIndexMap.keyAt(i), mIndexMax);
-                }
+            for (int i = 0; i < mIndexMap.size(); i++) {
+                mIndexMap.put(mIndexMap.keyAt(i), mIndexMax);
             }
         }
 
@@ -6201,15 +6203,17 @@
                 mCameraSoundForced = cameraSoundForced;
                 if (cameraSoundForcedChanged) {
                     if (!mIsSingleVolume) {
-                        VolumeStreamState s = mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED];
-                        if (cameraSoundForced) {
-                            s.setAllIndexesToMax();
-                            mRingerModeAffectedStreams &=
-                                    ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
-                        } else {
-                            s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM], TAG);
-                            mRingerModeAffectedStreams |=
-                                    (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+                        synchronized (VolumeStreamState.class) {
+                            VolumeStreamState s = mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED];
+                            if (cameraSoundForced) {
+                                s.setAllIndexesToMax();
+                                mRingerModeAffectedStreams &=
+                                        ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+                            } else {
+                                s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM], TAG);
+                                mRingerModeAffectedStreams |=
+                                        (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+                            }
                         }
                         // take new state into account for streams muted by ringer mode
                         setRingerModeInt(getRingerModeInternal(), false);
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 85b70ca..a24f97e 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -392,6 +392,15 @@
         return !isVPN() && numForegroundNetworkRequests() == 0 && mNumBackgroundNetworkRequests > 0;
     }
 
+    /**
+     * Returns whether this network is currently suspended. A network is suspended if it is still
+     * connected but data temporarily fails to transfer. See {@link NetworkInfo.State#SUSPENDED}
+     * and {@link NetworkCapabilities#NET_CAPABILITY_NOT_SUSPENDED}.
+     */
+    public boolean isSuspended() {
+        return networkInfo.getState() == NetworkInfo.State.SUSPENDED;
+    }
+
     // Does this network satisfy request?
     public boolean satisfies(NetworkRequest request) {
         return created &&
@@ -458,7 +467,7 @@
 
     public NetworkState getNetworkState() {
         synchronized (this) {
-            // Network objects are outwardly immutable so there is no point to duplicating.
+            // Network objects are outwardly immutable so there is no point in duplicating.
             // Duplicating also precludes sharing socket factories and connection pools.
             final String subscriberId = (networkMisc != null) ? networkMisc.subscriberId : null;
             return new NetworkState(new NetworkInfo(networkInfo),
diff --git a/services/core/java/com/android/server/content/SyncManagerConstants.java b/services/core/java/com/android/server/content/SyncManagerConstants.java
index 2f35687..3139d54 100644
--- a/services/core/java/com/android/server/content/SyncManagerConstants.java
+++ b/services/core/java/com/android/server/content/SyncManagerConstants.java
@@ -22,6 +22,8 @@
 import android.util.KeyValueListParser;
 import android.util.Slog;
 
+import com.android.internal.os.BackgroundThread;
+
 import java.io.PrintWriter;
 
 public class SyncManagerConstants extends ContentObserver {
@@ -57,9 +59,11 @@
     }
 
     public void start() {
-        mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
-                Settings.Global.SYNC_MANAGER_CONSTANTS), false, this);
-        refresh();
+        BackgroundThread.getHandler().post(() -> {
+            mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.SYNC_MANAGER_CONSTANTS), false, this);
+            refresh();
+        });
     }
 
     @Override
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index b5f94b1..3da3551 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -915,7 +915,7 @@
         notifyLockoutResetMonitors();
     }
 
-    private class FingerprintServiceLockoutResetMonitor {
+    private class FingerprintServiceLockoutResetMonitor implements IBinder.DeathRecipient {
 
         private static final long WAKELOCK_TIMEOUT_MS = 2000;
         private final IFingerprintServiceLockoutResetCallback mCallback;
@@ -926,6 +926,11 @@
             mCallback = callback;
             mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                     "lockout reset callback");
+            try {
+                mCallback.asBinder().linkToDeath(FingerprintServiceLockoutResetMonitor.this, 0);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "caught remote exception in linkToDeath", e);
+            }
         }
 
         public void sendLockoutReset() {
@@ -959,6 +964,12 @@
                 removeLockoutResetCallback(FingerprintServiceLockoutResetMonitor.this);
             }
         };
+
+        @Override
+        public void binderDied() {
+            Slog.e(TAG, "Lockout reset callback binder died");
+            mHandler.post(mRemoveCallbackRunnable);
+        }
     }
 
     private IBiometricsFingerprintClientCallback mDaemonCallback =
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index be48f69..c33d7f4 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -23,6 +23,7 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.IUidObserver;
 import android.app.job.IJobScheduler;
@@ -184,6 +185,7 @@
     IBatteryStats mBatteryStats;
     DeviceIdleController.LocalService mLocalDeviceIdleController;
     AppStateTracker mAppStateTracker;
+    final UsageStatsManagerInternal mUsageStats;
 
     /**
      * Set to true once we are allowed to run third party apps.
@@ -225,7 +227,10 @@
      */
     final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE };
     long mHeartbeat = 0;
-    long mLastHeartbeatTime = 0;
+    long mLastHeartbeatTime = sElapsedRealtimeClock.millis();
+
+    static final String HEARTBEAT_TAG = "*job.heartbeat*";
+    final HeartbeatAlarmListener mHeartbeatAlarm = new HeartbeatAlarmListener();
 
     // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
 
@@ -495,6 +500,9 @@
                 STANDBY_BEATS[3] = mParser.getInt(KEY_STANDBY_RARE_BEATS,
                         DEFAULT_STANDBY_RARE_BEATS);
             }
+
+            // Reset the heartbeat alarm based on the new heartbeat duration
+            setNextHeartbeatAlarm();
         }
 
         void dump(PrintWriter pw) {
@@ -1090,9 +1098,9 @@
         mJobSchedulerStub = new JobSchedulerStub();
 
         // Set up the app standby bucketing tracker
-        UsageStatsManagerInternal usageStats = LocalServices.getService(UsageStatsManagerInternal.class);
-        mStandbyTracker = new StandbyTracker(usageStats);
-        usageStats.addAppIdleStateChangeListener(mStandbyTracker);
+        mStandbyTracker = new StandbyTracker();
+        mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
+        mUsageStats.addAppIdleStateChangeListener(mStandbyTracker);
 
         // The job store needs to call back
         publishLocalService(JobSchedulerInternal.class, new LocalService());
@@ -1177,6 +1185,7 @@
 
             mAppStateTracker = Preconditions.checkNotNull(
                     LocalServices.getService(AppStateTracker.class));
+            setNextHeartbeatAlarm();
 
             // Register br for package removals and user removals.
             final IntentFilter filter = new IntentFilter();
@@ -1418,6 +1427,23 @@
                 periodicToReschedule.getLastFailedRunTime());
     }
 
+    long heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId) {
+        final long heartbeat;
+        final long timeSinceLastJob = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
+        synchronized (mLock) {
+            heartbeat = mHeartbeat - (timeSinceLastJob / mConstants.STANDBY_HEARTBEAT_TIME);
+        }
+        if (DEBUG_STANDBY) {
+            Slog.v(TAG, "Last job heartbeat " + heartbeat + " for " + packageName + "/" + userId
+                    + " delta=" + timeSinceLastJob);
+        }
+        return heartbeat;
+    }
+
+    long heartbeatWhenJobsLastRun(JobStatus job) {
+        return heartbeatWhenJobsLastRun(job.getSourcePackageName(), job.getSourceUserId());
+    }
+
     // JobCompletedListener implementations.
 
     /**
@@ -1560,9 +1586,7 @@
         noteJobsNonpending(mPendingJobs);
         mPendingJobs.clear();
         stopNonReadyActiveJobsLocked();
-        boolean updated = updateStandbyHeartbeatLocked();
         mJobs.forEachJob(mReadyQueueFunctor);
-        if (updated) updateNextStandbyHeartbeatsLocked();
         mReadyQueueFunctor.postProcess();
 
         if (DEBUG) {
@@ -1716,36 +1740,78 @@
         noteJobsNonpending(mPendingJobs);
         mPendingJobs.clear();
         stopNonReadyActiveJobsLocked();
-        boolean updated = updateStandbyHeartbeatLocked();
         mJobs.forEachJob(mMaybeQueueFunctor);
-        if (updated) updateNextStandbyHeartbeatsLocked();
         mMaybeQueueFunctor.postProcess();
     }
 
-    private boolean updateStandbyHeartbeatLocked() {
-        final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
-        final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
-        if (beatsElapsed > 0) {
-            mHeartbeat += beatsElapsed;
-            mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
-            if (DEBUG_STANDBY) {
-                Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed + " to " + mHeartbeat);
+    /**
+     * Heartbeat tracking.  The heartbeat alarm is intentionally non-wakeup.
+     */
+    class HeartbeatAlarmListener implements AlarmManager.OnAlarmListener {
+
+        @Override
+        public void onAlarm() {
+            synchronized (mLock) {
+                final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
+                final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
+                if (beatsElapsed > 0) {
+                    mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
+                    advanceHeartbeatLocked(beatsElapsed);
+                }
             }
-            return true;
+            setNextHeartbeatAlarm();
         }
-        return false;
     }
 
-    private void updateNextStandbyHeartbeatsLocked() {
-        // don't update ACTIVE or NEVER bucket milestones
+    // Intentionally does not touch the alarm timing
+    void advanceHeartbeatLocked(long beatsElapsed) {
+        mHeartbeat += beatsElapsed;
+        if (DEBUG_STANDBY) {
+            Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
+                    + " to " + mHeartbeat);
+        }
+        // Don't update ACTIVE or NEVER bucket milestones.  Note that mHeartbeat
+        // will be equal to mNextBucketHeartbeat[bucket] for one beat, during which
+        // new jobs scheduled by apps in that bucket will be permitted to run
+        // immediately.
+        boolean didAdvanceBucket = false;
         for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) {
-            while (mHeartbeat >= mNextBucketHeartbeat[i]) {
+            // Did we reach or cross a bucket boundary?
+            if (mHeartbeat >= mNextBucketHeartbeat[i]) {
+                didAdvanceBucket = true;
+            }
+            while (mHeartbeat > mNextBucketHeartbeat[i]) {
                 mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i];
             }
             if (DEBUG_STANDBY) {
-                Slog.v(TAG, "   Bucket " + i + " next heartbeat " + mNextBucketHeartbeat[i]);
+                Slog.v(TAG, "   Bucket " + i + " next heartbeat "
+                        + mNextBucketHeartbeat[i]);
             }
         }
+
+        if (didAdvanceBucket) {
+            if (DEBUG_STANDBY) {
+                Slog.v(TAG, "Hit bucket boundary; reevaluating job runnability");
+            }
+            mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+        }
+    }
+
+    void setNextHeartbeatAlarm() {
+        final long heartbeatLength;
+        synchronized (mLock) {
+            heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
+        }
+        final long now = sElapsedRealtimeClock.millis();
+        final long nextBeatOrdinal = (now + heartbeatLength) / heartbeatLength;
+        final long nextHeartbeat = nextBeatOrdinal * heartbeatLength;
+        if (DEBUG_STANDBY) {
+            Slog.i(TAG, "Setting heartbeat alarm for " + nextHeartbeat
+                    + " = " + TimeUtils.formatDuration(nextHeartbeat - now));
+        }
+        AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
+        am.setExact(AlarmManager.ELAPSED_REALTIME, nextHeartbeat,
+                HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
     }
 
     /**
@@ -1811,17 +1877,20 @@
         if (!mInParole && !job.getJob().isExemptedFromAppStandby()) {
             final int bucket = job.getStandbyBucket();
             if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
-                // Only skip this job if it's still waiting for the end of its (initial) nominal
+                // Only skip this job if the app is still waiting for the end of its nominal
                 // bucket interval.  Once it's waited that long, we let it go ahead and clear.
                 // The final (NEVER) bucket is special; we never age those apps' jobs into
                 // runnability.
+                final long appLastRan = heartbeatWhenJobsLastRun(job);
                 if (bucket >= mConstants.STANDBY_BEATS.length
-                        || (mHeartbeat < job.getBaseHeartbeat() + mConstants.STANDBY_BEATS[bucket])) {
+                        || (mHeartbeat > appLastRan
+                                && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
                     // TODO: log/trace that we're deferring the job due to bucketing if we hit this
                     if (job.getWhenStandbyDeferred() == 0) {
                         if (DEBUG_STANDBY) {
                             Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
-                                    + mNextBucketHeartbeat[job.getStandbyBucket()] + " for " + job);
+                                    + (appLastRan + mConstants.STANDBY_BEATS[bucket])
+                                    + " for " + job);
                         }
                         job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
                     }
@@ -2078,18 +2147,19 @@
                 // ACTIVE => everything can be run right away
                 // NEVER => we won't run them anyway, so let them go in the future
                 // as soon as the app enters normal use
+                if (DEBUG_STANDBY) {
+                    Slog.v(TAG, "Base heartbeat forced ZERO for new job in "
+                            + packageName + "/" + userId);
+                }
                 return 0;
             }
 
-            final long timeSinceLastJob = mStandbyTracker.getTimeSinceLastJobRun(
-                    packageName, userId);
-            final long bucketLength = mConstants.STANDBY_BEATS[appStandbyBucket];
-            final long bucketsAgo = timeSinceLastJob / bucketLength;
-
-            // If we haven't run any jobs for more than the app's current bucket period, just
-            // consider anything new to be immediately runnable.  Otherwise, base it on the
-            // bucket at which we last ran jobs.
-            return (bucketsAgo > bucketLength) ? 0 : (getCurrentHeartbeat() - bucketsAgo);
+            final long baseHeartbeat = heartbeatWhenJobsLastRun(packageName, userId);
+            if (DEBUG_STANDBY) {
+                Slog.v(TAG, "Base heartbeat " + baseHeartbeat + " for new job in "
+                        + packageName + "/" + userId);
+            }
+            return baseHeartbeat;
         }
 
         /**
@@ -2166,15 +2236,6 @@
      * Tracking of app assignments to standby buckets
      */
     final class StandbyTracker extends AppIdleStateChangeListener {
-        final UsageStatsManagerInternal mUsageStats;
-
-        StandbyTracker(UsageStatsManagerInternal usageStats) {
-            mUsageStats = usageStats;
-        }
-
-        public long getTimeSinceLastJobRun(String packageName, final @UserIdInt int userId) {
-            return mUsageStats.getTimeSinceLastJobRun(packageName, userId);
-        }
 
         // AppIdleStateChangeListener interface for live updates
 
@@ -2256,6 +2317,7 @@
         else return 0;
     }
 
+    // Static to support external callers
     public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
         UsageStatsManagerInternal usageStats = LocalServices.getService(
                 UsageStatsManagerInternal.class);
@@ -2682,6 +2744,7 @@
         }
     }
 
+    // Shell command infrastructure
     int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
         try {
             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
@@ -2759,6 +2822,21 @@
         return 0;
     }
 
+    // Shell command infrastructure
+    int executeHeartbeatCommand(PrintWriter pw, int numBeats) {
+        if (numBeats < 1) {
+            pw.println(getCurrentHeartbeat());
+            return 0;
+        }
+
+        pw.print("Advancing standby heartbeat by ");
+        pw.println(numBeats);
+        synchronized (mLock) {
+            advanceHeartbeatLocked(numBeats);
+        }
+        return 0;
+    }
+
     private String printContextIdToJobMap(JobStatus[] map, String initial) {
         StringBuilder s = new StringBuilder(initial + ": ");
         for (int i=0; i<map.length; i++) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerShellCommand.java b/services/core/java/com/android/server/job/JobSchedulerShellCommand.java
index d630aab..63225f3 100644
--- a/services/core/java/com/android/server/job/JobSchedulerShellCommand.java
+++ b/services/core/java/com/android/server/job/JobSchedulerShellCommand.java
@@ -64,6 +64,8 @@
                     return getStorageNotLow(pw);
                 case "get-job-state":
                     return getJobState(pw);
+                case "heartbeat":
+                    return doHeartbeat(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -333,6 +335,20 @@
         }
     }
 
+    private int doHeartbeat(PrintWriter pw) throws Exception {
+        checkPermission("manipulate scheduler heartbeat");
+
+        final String arg = getNextArg();
+        final int numBeats = (arg != null) ? Integer.parseInt(arg) : 0;
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            return mInternal.executeHeartbeatCommand(pw, numBeats);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     @Override
     public void onHelp() {
         final PrintWriter pw = getOutPrintWriter();
@@ -359,6 +375,9 @@
         pw.println("    Options:");
         pw.println("      -u or --user: specify which user's job is to be run; the default is");
         pw.println("         the primary or system user");
+        pw.println("  heartbeat [num]");
+        pw.println("    With no argument, prints the current standby heartbeat.  With a positive");
+        pw.println("    argument, advances the standby heartbeat by that number.");
         pw.println("  monitor-battery [on|off]");
         pw.println("    Control monitoring of all battery changes.  Off by default.  Turning");
         pw.println("    on makes get-battery-seq useful.");
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index 4988974..1f8cf76 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -240,11 +240,6 @@
                 }
             }
 
-            UsageStatsManagerInternal usageStats =
-                    LocalServices.getService(UsageStatsManagerInternal.class);
-            usageStats.setLastJobRunTime(job.getSourcePackageName(), job.getSourceUserId(),
-                    mExecutionStartTimeElapsed);
-
             // Once we'e begun executing a job, we by definition no longer care whether
             // it was inflated from disk with not-yet-coherent delay/deadline bounds.
             job.clearPersistedUtcTimes();
@@ -267,12 +262,16 @@
                 removeOpTimeOutLocked();
                 return false;
             }
+            mJobPackageTracker.noteActive(job);
             try {
                 mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
             } catch (RemoteException e) {
                 // Whatever.
             }
-            mJobPackageTracker.noteActive(job);
+            UsageStatsManagerInternal usageStats =
+                    LocalServices.getService(UsageStatsManagerInternal.class);
+            usageStats.setLastJobRunTime(job.getSourcePackageName(), job.getSourceUserId(),
+                    mExecutionStartTimeElapsed);
             mAvailable = false;
             mStoppedReason = null;
             mStoppedTime = 0;
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 08ff7bd..3867306 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -1346,6 +1346,15 @@
         }
         pw.print(prefix); pw.print("Standby bucket: ");
         pw.println(bucketName(standbyBucket));
+        if (standbyBucket > 0) {
+            pw.print(prefix); pw.print("Base heartbeat: ");
+            pw.println(baseHeartbeat);
+        }
+        if (whenStandbyDeferred != 0) {
+            pw.print(prefix); pw.print("  Deferred since: ");
+            TimeUtils.formatDuration(whenStandbyDeferred, elapsedRealtimeMillis, pw);
+            pw.println();
+        }
         pw.print(prefix); pw.print("Enqueue time: ");
         TimeUtils.formatDuration(enqueueTime, elapsedRealtimeMillis, pw);
         pw.println();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
index c9c9329..c4f1f3d 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
@@ -19,9 +19,6 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
 
-import com.android.internal.widget.LockPatternUtils;
-import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
-
 import android.app.AlarmManager;
 import android.app.AlarmManager.OnAlarmListener;
 import android.app.admin.DevicePolicyManager;
@@ -29,10 +26,9 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.fingerprint.FingerprintManager;
-import android.os.Binder;
-import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.Message;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -40,7 +36,7 @@
 import android.util.Slog;
 import android.util.SparseIntArray;
 
-import java.util.ArrayList;
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
 
 /**
  * Keeps track of requests for strong authentication.
@@ -58,7 +54,7 @@
     private static final String STRONG_AUTH_TIMEOUT_ALARM_TAG =
             "LockSettingsStrongAuth.timeoutForUser";
 
-    private final ArrayList<IStrongAuthTracker> mStrongAuthTrackers = new ArrayList<>();
+    private final RemoteCallbackList<IStrongAuthTracker> mTrackers = new RemoteCallbackList<>();
     private final SparseIntArray mStrongAuthForUser = new SparseIntArray();
     private final ArrayMap<Integer, StrongAuthTimeoutAlarmListener>
             mStrongAuthTimeoutAlarmListenerForUser = new ArrayMap<>();
@@ -82,12 +78,7 @@
     }
 
     private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) {
-        for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
-            if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) {
-                return;
-            }
-        }
-        mStrongAuthTrackers.add(tracker);
+        mTrackers.register(tracker);
 
         for (int i = 0; i < mStrongAuthForUser.size(); i++) {
             int key = mStrongAuthForUser.keyAt(i);
@@ -101,12 +92,7 @@
     }
 
     private void handleRemoveStrongAuthTracker(IStrongAuthTracker tracker) {
-        for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
-            if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) {
-                mStrongAuthTrackers.remove(i);
-                return;
-            }
-        }
+        mTrackers.unregister(tracker);
     }
 
     private void handleRequireStrongAuth(int strongAuthReason, int userId) {
@@ -157,16 +143,19 @@
     }
 
     private void notifyStrongAuthTrackers(int strongAuthReason, int userId) {
-        for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
-            try {
-                mStrongAuthTrackers.get(i).onStrongAuthRequiredChanged(strongAuthReason, userId);
-            } catch (DeadObjectException e) {
-                Slog.d(TAG, "Removing dead StrongAuthTracker.");
-                mStrongAuthTrackers.remove(i);
+        int i = mTrackers.beginBroadcast();
+        try {
+            while (i > 0) {
                 i--;
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Exception while notifying StrongAuthTracker.", e);
+                try {
+                    mTrackers.getBroadcastItem(i).onStrongAuthRequiredChanged(
+                            strongAuthReason, userId);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Exception while notifying StrongAuthTracker.", e);
+                }
             }
+        } finally {
+            mTrackers.finishBroadcast();
         }
     }
 
@@ -243,4 +232,5 @@
             }
         }
     };
+
 }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS b/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS
new file mode 100644
index 0000000..bb487fb
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/OWNERS
@@ -0,0 +1,4 @@
+aseemk@google.com
+bozhu@google.com
+dementyev@google.com
+robertberry@google.com
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 152c910..23a66ba 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -170,11 +170,13 @@
             certXml = CertXml.parse(recoveryServiceCertFile);
         } catch (CertParsingException e) {
             // TODO: Do not use raw key bytes anymore once the other components are updated
-            Log.d(TAG, "Failed to parse the cert file", e);
+            Log.d(TAG, "Failed to parse the input as a cert file: " + HexDump.toHexString(
+                    recoveryServiceCertFile));
             PublicKey publicKey = parseEcPublicKey(recoveryServiceCertFile);
             if (mDatabase.setRecoveryServicePublicKey(userId, uid, publicKey) > 0) {
                 mDatabase.setShouldCreateSnapshot(userId, uid, true);
             }
+            Log.d(TAG, "Successfully set the input as the raw public key");
             return;
         }
 
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index f346629..e2b2d46 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -145,6 +145,7 @@
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -2759,6 +2760,13 @@
             return;
         }
 
+        // Fourth check: is caller a testing app on a debug build?
+        final boolean enableDebug = Build.IS_USERDEBUG || Build.IS_ENG;
+        if (enableDebug && callingPackage
+                .equals(SystemProperties.get("fw.sub_plan_owner." + subId, null))) {
+            return;
+        }
+
         // Final check: does the caller hold a permission?
         mContext.enforceCallingOrSelfPermission(MANAGE_SUBSCRIPTION_PLANS, TAG);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7ca6bb95c..b9f6d21 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -20644,10 +20644,6 @@
     @Override
     public String getInstallerPackageName(String packageName) {
         final int callingUid = Binder.getCallingUid();
-        if (getInstantAppPackageName(callingUid) != null) {
-            return null;
-        }
-        // reader
         synchronized (mPackages) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
             if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index a0577b1..0eeaf66 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1486,6 +1486,23 @@
         return restrictions != null && restrictions.getBoolean(restrictionKey);
     }
 
+    /** @return if any user has the given restriction. */
+    @Override
+    public boolean hasUserRestrictionOnAnyUser(String restrictionKey) {
+        if (!UserRestrictionsUtils.isValidRestriction(restrictionKey)) {
+            return false;
+        }
+        final List<UserInfo> users = getUsers(/* excludeDying= */ true);
+        for (int i = 0; i < users.size(); i++) {
+            final int userId = users.get(i).id;
+            Bundle restrictions = getEffectiveUserRestrictions(userId);
+            if (restrictions != null && restrictions.getBoolean(restrictionKey)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * @hide
      *
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 23185d7..41570c4 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -33,6 +33,7 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.provider.Settings;
+import android.provider.Settings.Global;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.util.Log;
@@ -581,6 +582,15 @@
                                 Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP, "0");
                     }
                     break;
+                case UserManager.DISALLOW_CONFIG_LOCATION:
+                    // When DISALLOW_CONFIG_LOCATION is set on any user, we undo the global
+                    // kill switch.
+                    if (newValue) {
+                        android.provider.Settings.Global.putString(
+                                context.getContentResolver(),
+                                Global.LOCATION_GLOBAL_KILL_SWITCH, "0");
+                    }
+                    break;
             }
         } finally {
             Binder.restoreCallingIdentity(id);
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
index 37df94f..5d76329 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.power.batterysaver;
 
+import android.metrics.LogMaker;
 import android.os.BatteryManagerInternal;
 import android.os.SystemClock;
 import android.util.ArrayMap;
@@ -23,6 +24,8 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.power.BatterySaverPolicy;
@@ -43,6 +46,9 @@
 
     private static final boolean DEBUG = BatterySaverPolicy.DEBUG;
 
+    @VisibleForTesting
+    static final boolean SEND_TRON_EVENTS = true;
+
     private final Object mLock = new Object();
 
     /** Whether battery saver is on or off. */
@@ -132,15 +138,6 @@
         }
     }
 
-    @VisibleForTesting
-    static final String COUNTER_POWER_PERCENT_PREFIX = "battery_saver_stats_percent_";
-
-    @VisibleForTesting
-    static final String COUNTER_POWER_MILLIAMPS_PREFIX = "battery_saver_stats_milliamps_";
-
-    @VisibleForTesting
-    static final String COUNTER_TIME_SECONDS_PREFIX = "battery_saver_stats_seconds_";
-
     private static BatterySavingStats sInstance;
 
     private BatteryManagerInternal mBatteryManagerInternal;
@@ -427,10 +424,9 @@
             if (stateChanging) {
                 if (mLastState >= 0) {
                     final long deltaTime = now - mStartTime;
-                    final int deltaBattery = mStartBatteryLevel - batteryLevel;
-                    final int deltaPercent = mStartPercent - batteryPercent;
 
-                    report(mLastState, deltaTime, deltaBattery, deltaPercent);
+                    report(mLastState, deltaTime, mStartBatteryLevel, mStartPercent,
+                            batteryLevel, batteryPercent);
                 }
                 mStartTime = now;
                 mStartBatteryLevel = batteryLevel;
@@ -439,23 +435,28 @@
             mLastState = newState;
         }
 
-        String getCounterSuffix(int state) {
-            final boolean batterySaver =
+        void report(int state, long deltaTimeMs,
+                int startBatteryLevelUa, int startBatteryLevelPercent,
+                int endBatteryLevelUa, int endBatteryLevelPercent) {
+            if (!SEND_TRON_EVENTS) {
+                return;
+            }
+            final boolean batterySaverOn =
                     BatterySaverState.fromIndex(state) != BatterySaverState.OFF;
             final boolean interactive =
                     InteractiveState.fromIndex(state) != InteractiveState.NON_INTERACTIVE;
-            if (batterySaver) {
-                return interactive ? "11" : "10";
-            } else {
-                return interactive ? "01" : "00";
-            }
-        }
 
-        void report(int state, long deltaTimeMs, int deltaBatteryUa, int deltaPercent) {
-            final String suffix = getCounterSuffix(state);
-            mMetricsLogger.count(COUNTER_POWER_MILLIAMPS_PREFIX + suffix, deltaBatteryUa / 1000);
-            mMetricsLogger.count(COUNTER_POWER_PERCENT_PREFIX + suffix, deltaPercent);
-            mMetricsLogger.count(COUNTER_TIME_SECONDS_PREFIX + suffix, (int) (deltaTimeMs / 1000));
+            final LogMaker logMaker = new LogMaker(MetricsProto.MetricsEvent.BATTERY_SAVER)
+                    .setSubtype(batterySaverOn ? 1 : 0)
+                    .addTaggedData(MetricsEvent.FIELD_INTERACTIVE, interactive ? 1 : 0)
+                    .addTaggedData(MetricsEvent.FIELD_DURATION_MILLIS, deltaTimeMs)
+                    .addTaggedData(MetricsEvent.FIELD_START_BATTERY_UA, startBatteryLevelUa)
+                    .addTaggedData(MetricsEvent.FIELD_START_BATTERY_PERCENT,
+                            startBatteryLevelPercent)
+                    .addTaggedData(MetricsEvent.FIELD_END_BATTERY_UA, endBatteryLevelUa)
+                    .addTaggedData(MetricsEvent.FIELD_END_BATTERY_PERCENT, endBatteryLevelPercent);
+
+            mMetricsLogger.write(logMaker);
         }
     }
 }
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 9f9b1af..fa7e535 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -172,7 +172,6 @@
     public void sendSubscriberBroadcast(IBinder intentSenderBinder, long configUid, long configKey,
                                         long subscriptionId, long subscriptionRuleId,
                                         StatsDimensionsValue dimensionsValue) {
-        if (DEBUG) Slog.d(TAG, "Statsd requested to sendSubscriberBroadcast.");
         enforceCallingPermission();
         IntentSender intentSender = new IntentSender(intentSenderBinder);
         Intent intent = new Intent()
@@ -181,16 +180,16 @@
                 .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_ID, subscriptionId)
                 .putExtra(StatsManager.EXTRA_STATS_SUBSCRIPTION_RULE_ID, subscriptionRuleId)
                 .putExtra(StatsManager.EXTRA_STATS_DIMENSIONS_VALUE, dimensionsValue);
+        if (DEBUG) {
+            Slog.d(TAG, String.format("Statsd sendSubscriberBroadcast with params {%d %d %d %d %s}",
+                    configUid, configKey, subscriptionId,
+                    subscriptionRuleId, dimensionsValue));
+        }
         try {
             intentSender.sendIntent(mContext, CODE_SUBSCRIBER_BROADCAST, intent, null, null);
         } catch (IntentSender.SendIntentException e) {
             Slog.w(TAG, "Unable to send using IntentSender from uid " + configUid
                     + "; presumably it had been cancelled.");
-            if (DEBUG) {
-                Slog.d(TAG, String.format("SubscriberBroadcast params {%d %d %d %d %s}",
-                        configUid, configKey, subscriptionId,
-                        subscriptionRuleId, dimensionsValue));
-            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index ce3f512..8155656 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1766,6 +1766,9 @@
             layer += Z_BOOST_BASE;
         }
         leash.setLayer(layer);
+
+        final DisplayContent dc = getDisplayContent();
+        dc.assignStackOrdering(t);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f421bf4..2d32c81 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3524,39 +3524,47 @@
 
         @Override
         void assignChildLayers(SurfaceControl.Transaction t) {
+            assignStackOrdering(t);
 
+            for (int i = 0; i < mChildren.size(); i++) {
+                final TaskStack s = mChildren.get(i);
+                s.assignChildLayers(t);
+            }
+        }
+
+        void assignStackOrdering(SurfaceControl.Transaction t) {
             final int HOME_STACK_STATE = 0;
             final int NORMAL_STACK_STATE = 1;
             final int ALWAYS_ON_TOP_STATE = 2;
 
             int layer = 0;
+            int layerForAnimationLayer = 0;
+
             for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) {
                 for (int i = 0; i < mChildren.size(); i++) {
                     final TaskStack s = mChildren.get(i);
-                    if (state == HOME_STACK_STATE && s.isActivityTypeHome()) {
-                        s.assignLayer(t, layer++);
-                    } else if (state == NORMAL_STACK_STATE && !s.isActivityTypeHome()
-                            && !s.isAlwaysOnTop()) {
-                        s.assignLayer(t, layer++);
-                        if (s.inSplitScreenWindowingMode() && mSplitScreenDividerAnchor != null) {
-                            t.setLayer(mSplitScreenDividerAnchor, layer++);
-                        }
-                    } else if (state == ALWAYS_ON_TOP_STATE && s.isAlwaysOnTop()) {
-                        s.assignLayer(t, layer++);
+                    if (state == HOME_STACK_STATE && !s.isActivityTypeHome()) {
+                        continue;
+                    } else if (state == NORMAL_STACK_STATE && (s.isActivityTypeHome()
+                            || s.isAlwaysOnTop())) {
+                        continue;
+                    } else if (state == ALWAYS_ON_TOP_STATE && !s.isAlwaysOnTop()) {
+                        continue;
+                    }
+                    s.assignLayer(t, layer++);
+                    if (s.inSplitScreenWindowingMode() && mSplitScreenDividerAnchor != null) {
+                        t.setLayer(mSplitScreenDividerAnchor, layer++);
+                    }
+                    if (s.isSelfOrChildAnimating()) {
+                        // Ensure the animation layer ends up above the
+                        // highest animating stack and no higher.
+                        layerForAnimationLayer = layer++;
                     }
                 }
-                // The appropriate place for App-Transitions to occur is right
-                // above all other animations but still below things in the Picture-and-Picture
-                // windowing mode.
-                if (state == NORMAL_STACK_STATE && mAppAnimationLayer != null) {
-                    t.setLayer(mAppAnimationLayer, layer++);
-                }
             }
-            for (int i = 0; i < mChildren.size(); i++) {
-                final TaskStack s = mChildren.get(i);
-                s.assignChildLayers(t);
+            if (mAppAnimationLayer != null) {
+                t.setLayer(mAppAnimationLayer, layerForAnimationLayer);
             }
-
         }
 
         @Override
@@ -3854,4 +3862,8 @@
 
         super.prepareSurfaces();
     }
+
+    void assignStackOrdering(SurfaceControl.Transaction t) {
+        mTaskStackContainers.assignStackOrdering(t);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index fa4474b..93e9137 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1129,7 +1129,7 @@
         scheduleAnimation();
     }
 
-    private void reassignLayer(Transaction t) {
+    void reassignLayer(Transaction t) {
         final WindowContainer parent = getParent();
         if (parent != null) {
             parent.assignChildLayers(t);
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
index 44ac803..503adb2 100644
--- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -19,14 +19,12 @@
 import static com.android.server.backup.testing.TransportData.genericTransport;
 import static com.android.server.backup.testing.TransportTestUtils.mockTransport;
 import static com.android.server.backup.testing.TransportTestUtils.setUpTransportsForTransportManager;
+
 import static com.google.common.truth.Truth.assertThat;
-import static java.util.Arrays.asList;
-import static java.util.Collections.emptyList;
-import static java.util.Collections.singletonList;
-import static java.util.stream.Collectors.toList;
-import static java.util.stream.Collectors.toSet;
-import static java.util.stream.Stream.concat;
+
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -35,6 +33,13 @@
 import static org.robolectric.shadow.api.Shadow.extract;
 import static org.testng.Assert.expectThrows;
 
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singletonList;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static java.util.stream.Stream.concat;
+
 import android.annotation.Nullable;
 import android.app.backup.BackupManager;
 import android.content.ComponentName;
@@ -43,6 +48,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.platform.test.annotations.Presubmit;
+
 import com.android.server.backup.testing.ShadowContextImplForBackup;
 import com.android.server.backup.testing.TransportData;
 import com.android.server.backup.testing.TransportTestUtils.TransportMock;
@@ -54,11 +60,7 @@
 import com.android.server.testing.SystemLoaderPackages;
 import com.android.server.testing.shadows.FrameworkShadowContextImpl;
 import com.android.server.testing.shadows.FrameworkShadowPackageManager;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Stream;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -69,6 +71,12 @@
 import org.robolectric.annotation.Config;
 import org.robolectric.shadows.ShadowPackageManager;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Stream;
+
 @RunWith(FrameworkRobolectricTestRunner.class)
 @Config(
     manifest = Config.NONE,
@@ -81,6 +89,12 @@
     private static final String PACKAGE_A = "some.package.a";
     private static final String PACKAGE_B = "some.package.b";
 
+    /**
+     * GMSCore depends on this constant so we define it here on top of the definition in
+     * {@link TransportManager} to verify this extra is passed
+     */
+    private static final String EXTRA_TRANSPORT_REGISTRATION = "transport_registration";
+
     @Mock private OnTransportRegisteredListener mListener;
     @Mock private TransportClientManager mTransportClientManager;
     private TransportData mTransportA1;
@@ -195,6 +209,22 @@
     }
 
     @Test
+    public void testRegisterTransports_passesRegistrationExtraToGetTransportClient()
+            throws Exception {
+        setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+        setUpTransports(mTransportA1);
+        TransportManager transportManager = createTransportManager(mTransportA1);
+
+        transportManager.registerTransports();
+
+        verify(mTransportClientManager)
+                .getTransportClient(
+                        eq(mTransportA1.getTransportComponent()),
+                        argThat(bundle -> bundle.getBoolean(EXTRA_TRANSPORT_REGISTRATION)),
+                        anyString());
+    }
+
+    @Test
     public void testOnPackageAdded_registerTransports() throws Exception {
         setUpPackage(PACKAGE_A, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
         setUpTransports(mTransportA1);
@@ -580,6 +610,9 @@
             when(mTransportClientManager.getTransportClient(
                             eq(transport.getTransportComponent()), any()))
                     .thenReturn(transportMock.transportClient);
+            when(mTransportClientManager.getTransportClient(
+                            eq(transport.getTransportComponent()), any(), any()))
+                    .thenReturn(transportMock.transportClient);
             transportMocks.add(transportMock);
         }
         return transportMocks;
diff --git a/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java b/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java
new file mode 100644
index 0000000..5e3c974
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/transport/TransportClientManagerTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 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 com.android.server.backup.transport;
+
+import static com.android.server.backup.TransportManager.SERVICE_ACTION_TRANSPORT_HOST;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class TransportClientManagerTest {
+
+    private static final String PACKAGE_NAME = "random.package.name";
+    private static final String CLASS_NAME = "random.package.name.transport.Transport";
+
+    @Mock private Context mContext;
+    @Mock private TransportConnectionListener mTransportConnectionListener;
+    private TransportClientManager mTransportClientManager;
+    private ComponentName mTransportComponent;
+    private Intent mBindIntent;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mTransportClientManager = new TransportClientManager(mContext);
+        mTransportComponent = new ComponentName(PACKAGE_NAME, CLASS_NAME);
+        mBindIntent = new Intent(SERVICE_ACTION_TRANSPORT_HOST).setComponent(mTransportComponent);
+
+        when(mContext.bindServiceAsUser(
+                        any(Intent.class),
+                        any(ServiceConnection.class),
+                        anyInt(),
+                        any(UserHandle.class)))
+                .thenReturn(true);
+    }
+
+    @Test
+    public void testGetTransportClient_withExtras_createsTransportClientWithCorrectIntent() {
+        Bundle extras = new Bundle();
+        extras.putBoolean("random_extra", true);
+        mBindIntent.putExtras(extras);
+
+        TransportClient transportClient =
+                mTransportClientManager.getTransportClient(mTransportComponent, extras, "caller");
+
+        transportClient.connectAsync(mTransportConnectionListener, "caller");
+        verify(mContext)
+                .bindServiceAsUser(
+                        argThat(matchesIntentAndExtras(mBindIntent)),
+                        any(ServiceConnection.class),
+                        anyInt(),
+                        any(UserHandle.class));
+    }
+
+    private ArgumentMatcher<Intent> matchesIntentAndExtras(Intent expectedIntent) {
+        return (Intent actualIntent) -> {
+            if (!expectedIntent.filterEquals(actualIntent)) {
+                return false;
+            }
+
+            Bundle expectedExtras = expectedIntent.getExtras();
+            Bundle actualExtras = actualIntent.getExtras();
+
+            if (expectedExtras == null && actualExtras == null) {
+                return true;
+            }
+
+            if (expectedExtras == null || actualExtras == null) {
+                return false;
+            }
+
+            if (expectedExtras.size() != actualExtras.size()) {
+                return false;
+            }
+
+            for (String key : expectedExtras.keySet()) {
+                if (!expectedExtras.get(key).equals(actualExtras.get(key))) {
+                    return false;
+                }
+            }
+
+            return true;
+        };
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
index 07262e1..23fe0ff 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
@@ -34,7 +34,6 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
-import android.os.Handler;
 import android.os.Message;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
@@ -49,9 +48,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.concurrent.CompletableFuture;
 import java.util.function.IntConsumer;
-import java.util.function.Supplier;
 
 
 /**
@@ -283,6 +280,19 @@
         verify(mMgh.getNext(), times(2)).onMotionEvent(any(), any(), anyInt());
     }
 
+    @Test
+    public void testTripleTapAndHold_zoomsImmediately() {
+        assertZoomsImmediatelyOnSwipeFrom(STATE_2TAPS);
+        assertZoomsImmediatelyOnSwipeFrom(STATE_SHORTCUT_TRIGGERED);
+    }
+
+    private void assertZoomsImmediatelyOnSwipeFrom(int state) {
+        goFromStateIdleTo(state);
+        swipeAndHold();
+        assertIn(STATE_DRAGGING_TMP);
+        returnToNormalFrom(STATE_DRAGGING_TMP);
+    }
+
     private void assertTransition(int fromState, Runnable transitionAction, int toState) {
         goFromStateIdleTo(fromState);
         transitionAction.run();
@@ -339,11 +349,13 @@
                 check(tapCount() == 2, state);
             } break;
             case STATE_DRAGGING: {
+                check(isZoomed(), state);
                 check(mMgh.mCurrentState == mMgh.mViewportDraggingState,
                         state);
                 check(mMgh.mViewportDraggingState.mZoomedInBeforeDrag, state);
             } break;
             case STATE_DRAGGING_TMP: {
+                check(isZoomed(), state);
                 check(mMgh.mCurrentState == mMgh.mViewportDraggingState,
                         state);
                 check(!mMgh.mViewportDraggingState.mZoomedInBeforeDrag, state);
@@ -353,11 +365,13 @@
                 check(!isZoomed(), state);
             } break;
             case STATE_PANNING: {
+                check(isZoomed(), state);
                 check(mMgh.mCurrentState == mMgh.mPanningScalingState,
                         state);
                 check(!mMgh.mPanningScalingState.mScaling, state);
             } break;
             case STATE_SCALING_AND_PANNING: {
+                check(isZoomed(), state);
                 check(mMgh.mCurrentState == mMgh.mPanningScalingState,
                         state);
                 check(mMgh.mPanningScalingState.mScaling, state);
diff --git a/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java b/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
new file mode 100644
index 0000000..2baf995
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/PendingRemoteAnimationRegistryTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 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 com.android.server.am;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.os.Handler;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.ArrayMap;
+import android.view.RemoteAnimationAdapter;
+
+import com.android.server.testutils.OffsettableClock;
+import com.android.server.testutils.TestHandler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * atest PendingRemoteAnimationRegistryTest
+ */
+@SmallTest
+@Presubmit
+@FlakyTest
+@RunWith(AndroidJUnit4.class)
+public class PendingRemoteAnimationRegistryTest extends ActivityTestsBase {
+
+    @Mock RemoteAnimationAdapter mAdapter;
+    private PendingRemoteAnimationRegistry mRegistry;
+    private final OffsettableClock mClock = new OffsettableClock.Stopped();
+    private TestHandler mHandler;
+    private ActivityManagerService mService;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        mService = createActivityManagerService();
+        mService.mHandlerThread.getThreadHandler().runWithScissors(() -> {
+            mHandler = new TestHandler(null, mClock);
+        }, 0);
+        mRegistry = new PendingRemoteAnimationRegistry(mService, mHandler);
+    }
+
+    @Test
+    public void testOverrideActivityOptions() {
+        mRegistry.addPendingAnimation("com.android.test", mAdapter);
+        ActivityOptions opts = ActivityOptions.makeBasic();
+        opts = mRegistry.overrideOptionsIfNeeded("com.android.test", opts);
+        assertEquals(mAdapter, opts.getRemoteAnimationAdapter());
+    }
+
+    @Test
+    public void testOverrideActivityOptions_null() {
+        mRegistry.addPendingAnimation("com.android.test", mAdapter);
+        final ActivityOptions opts = mRegistry.overrideOptionsIfNeeded("com.android.test", null);
+        assertNotNull(opts);
+        assertEquals(mAdapter, opts.getRemoteAnimationAdapter());
+    }
+
+    @Test
+    public void testTimeout() {
+        mRegistry.addPendingAnimation("com.android.test", mAdapter);
+        mClock.fastForward(5000);
+        mHandler.timeAdvance();
+        assertNull(mRegistry.overrideOptionsIfNeeded("com.android.test", null));
+    }
+
+    @Test
+    public void testTimeout_overridenEntry() {
+        mRegistry.addPendingAnimation("com.android.test", mAdapter);
+        mClock.fastForward(2500);
+        mHandler.timeAdvance();
+        mRegistry.addPendingAnimation("com.android.test", mAdapter);
+        mClock.fastForward(1000);
+        mHandler.timeAdvance();
+        final ActivityOptions opts = mRegistry.overrideOptionsIfNeeded("com.android.test", null);
+        assertEquals(mAdapter, opts.getRemoteAnimationAdapter());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/OWNERS b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/OWNERS
new file mode 100644
index 0000000..bb487fb
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/OWNERS
@@ -0,0 +1,4 @@
+aseemk@google.com
+bozhu@google.com
+dementyev@google.com
+robertberry@google.com
diff --git a/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java b/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java
index 6e1808b..28b54ef 100644
--- a/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/ConnOnActivityStartTest.java
@@ -323,6 +323,7 @@
 
     private void turnBatteryOn() throws Exception {
         executeCommand("cmd battery unplug");
+        executeCommand("cmd battery set status " + BatteryManager.BATTERY_STATUS_NOT_CHARGING);
         assertBatteryOn();
     }
 
@@ -336,6 +337,7 @@
 
     private void turnBatteryOff() throws Exception {
         executeCommand("cmd battery set ac " + BatteryManager.BATTERY_PLUGGED_AC);
+        executeCommand("cmd battery set status " + BatteryManager.BATTERY_STATUS_CHARGING);
     }
 
     private static void batteryReset() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
index f7516b2..f7112d4 100644
--- a/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
@@ -15,25 +15,30 @@
  */
 package com.android.server.power.batterysaver;
 
+import static com.android.server.power.batterysaver.BatterySavingStats.SEND_TRON_EVENTS;
+
 import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.metrics.LogMaker;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.server.power.batterysaver.BatterySavingStats.BatterySaverState;
 import com.android.server.power.batterysaver.BatterySavingStats.DozeState;
 import com.android.server.power.batterysaver.BatterySavingStats.InteractiveState;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
@@ -222,8 +227,28 @@
                 target.toDebugString());
     }
 
-    private void assertMetricsLog(String counter, int value) {
-        verify(mMetricsLogger, times(1)).count(eq(counter), eq(value));
+    private void assertLog(boolean batterySaver, boolean interactive, long deltaTimeMs,
+            int deltaBatteryLevelUa, int deltaBatteryLevelPercent) {
+        if (SEND_TRON_EVENTS) {
+            ArgumentCaptor<LogMaker> ac = ArgumentCaptor.forClass(LogMaker.class);
+            verify(mMetricsLogger, times(1)).write(ac.capture());
+
+            LogMaker lm = ac.getValue();
+            assertEquals(MetricsEvent.BATTERY_SAVER, lm.getCategory());
+            assertEquals(batterySaver ? 1 : 0,
+                    lm.getTaggedData(MetricsEvent.RESERVED_FOR_LOGBUILDER_SUBTYPE));
+            assertEquals(interactive ? 1 : 0, lm.getTaggedData(MetricsEvent.FIELD_INTERACTIVE));
+            assertEquals(deltaTimeMs, lm.getTaggedData(MetricsEvent.FIELD_DURATION_MILLIS));
+
+            assertEquals(deltaBatteryLevelUa,
+                    (int) lm.getTaggedData(MetricsEvent.FIELD_START_BATTERY_UA)
+                            - (int) lm.getTaggedData(MetricsEvent.FIELD_END_BATTERY_UA));
+            assertEquals(deltaBatteryLevelPercent,
+                    (int) lm.getTaggedData(MetricsEvent.FIELD_START_BATTERY_PERCENT)
+                            - (int) lm.getTaggedData(MetricsEvent.FIELD_END_BATTERY_PERCENT));
+        } else {
+            verify(mMetricsLogger, times(0)).write(any(LogMaker.class));
+        }
     }
 
     @Test
@@ -249,9 +274,7 @@
                 InteractiveState.NON_INTERACTIVE,
                 DozeState.NOT_DOZING);
 
-        assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "01", 2);
-        assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "01", 200);
-        assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "01", 60);
+        assertLog(false, true, 60_000, 2000, 200);
 
         target.advanceClock(1);
         target.drainBattery(2000);
@@ -282,9 +305,7 @@
                 InteractiveState.INTERACTIVE,
                 DozeState.NOT_DOZING);
 
-        assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "00", 2 * 3);
-        assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "00", 200 * 3);
-        assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "00", 60 * 3);
+        assertLog(false, false, 60_000 * 3, 2000 * 3, 200 * 3);
 
         target.advanceClock(10);
         target.drainBattery(10000);
@@ -292,9 +313,7 @@
         reset(mMetricsLogger);
         target.startCharging();
 
-        assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "11", 10);
-        assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "11", 1000);
-        assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "11", 60 * 10);
+        assertLog(true, true, 60_000 * 10, 10000, 1000);
 
         target.advanceClock(1);
         target.drainBattery(2000);
@@ -312,8 +331,6 @@
 
         target.startCharging();
 
-        assertMetricsLog(BatterySavingStats.COUNTER_POWER_MILLIAMPS_PREFIX + "10", 2);
-        assertMetricsLog(BatterySavingStats.COUNTER_POWER_PERCENT_PREFIX + "10", 200);
-        assertMetricsLog(BatterySavingStats.COUNTER_TIME_SECONDS_PREFIX + "10", 60);
+        assertLog(true, false, 60_000, 2000, 200);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java b/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java
index 029d9f1..1222b59 100644
--- a/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java
+++ b/services/tests/servicestests/src/com/android/server/testutils/TestHandler.java
@@ -16,10 +16,11 @@
 package com.android.server.testutils;
 
 
-import static android.util.ExceptionUtils.getRootCause;
+import static android.util.ExceptionUtils.appendCause;
 import static android.util.ExceptionUtils.propagate;
 
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
 import android.util.ArrayMap;
@@ -60,7 +61,7 @@
     }
 
     public TestHandler(Callback callback, LongSupplier clock) {
-        super(callback);
+        super(Looper.getMainLooper(), callback);
         mClock = clock;
     }
 
@@ -132,7 +133,7 @@
         } catch (Throwable t) {
             // Append stack trace of this message being posted as a cause for a helpful
             // test error message
-            throw propagate(getRootCause(t).initCause(msg.postPoint));
+            throw propagate(appendCause(t, msg.postPoint));
         } finally {
             msg.message.recycle();
         }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7afd28c..fefc03d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -53,6 +53,7 @@
 import android.telephony.ims.aidl.IImsRcsFeature;
 import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.util.Log;
 
 import com.android.ims.internal.IImsServiceFeatureCallback;
@@ -6410,84 +6411,106 @@
         return false;
     }
 
-   /**
-    * Returns the IMS Registration Status
-    * @hide
-    */
-   public boolean isImsRegistered() {
-       try {
-           ITelephony telephony = getITelephony();
-           if (telephony == null)
-               return false;
-           return telephony.isImsRegistered();
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
-           return false;
-       }
-   }
-
     /**
-     * Returns the IMS Registration Status for a particular Subscription ID
+     * Returns the IMS Registration Status for a particular Subscription ID.
      *
      * @param subId Subscription ID
      * @return true if IMS status is registered, false if the IMS status is not registered or a
      * RemoteException occurred.
-     *
      * @hide
      */
     public boolean isImsRegistered(int subId) {
-       try {
-           return getITelephony().isImsRegisteredForSubscriber(subId);
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
-           return false;
-       }
-    }
-
-    /**
-     * Returns the Status of Volte
-     * @hide
-     */
-    public boolean isVolteAvailable() {
-       try {
-           return getITelephony().isVolteAvailable();
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
-           return false;
-       }
-   }
-
-    /**
-     * Returns the Status of video telephony (VT)
-     * @hide
-     */
-    public boolean isVideoTelephonyAvailable() {
         try {
-            return getITelephony().isVideoTelephonyAvailable();
-        } catch (RemoteException ex) {
-            return false;
-        } catch (NullPointerException ex) {
+            return getITelephony().isImsRegistered(subId);
+        } catch (RemoteException | NullPointerException ex) {
             return false;
         }
     }
 
     /**
-     * Returns the Status of Wi-Fi Calling
+     * Returns the IMS Registration Status for a particular Subscription ID, which is determined
+     * when the TelephonyManager is created using {@link #createForSubscriptionId(int)}. If an
+     * invalid subscription ID is used during creation, will the default subscription ID will be
+     * used.
+     *
+     * @return true if IMS status is registered, false if the IMS status is not registered or a
+     * RemoteException occurred.
+     * @see SubscriptionManager#getDefaultSubscriptionId()
+     * @hide
+     */
+    public boolean isImsRegistered() {
+       try {
+           return getITelephony().isImsRegistered(getSubId());
+       } catch (RemoteException | NullPointerException ex) {
+           return false;
+       }
+    }
+
+    /**
+     * The current status of Voice over LTE for the subscription associated with this instance when
+     * it was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was
+     * used during creation, the default subscription ID will be used.
+     * @return true if Voice over LTE is available or false if it is unavailable or unknown.
+     * @see SubscriptionManager#getDefaultSubscriptionId()
+     * @hide
+     */
+    public boolean isVolteAvailable() {
+        try {
+            return getITelephony().isVolteAvailable(getSubId());
+        } catch (RemoteException | NullPointerException ex) {
+            return false;
+        }
+    }
+
+    /**
+     * The availability of Video Telephony (VT) for the subscription ID specified when this instance
+     * was created using {@link #createForSubscriptionId(int)}. If an invalid subscription ID was
+     * used during creation, the default subscription ID will be used. To query the
+     * underlying technology that VT is available on, use {@link #getImsRegTechnologyForMmTel}.
+     * @return true if VT is available, or false if it is unavailable or unknown.
+     * @hide
+     */
+    public boolean isVideoTelephonyAvailable() {
+        try {
+            return getITelephony().isVideoTelephonyAvailable(getSubId());
+        } catch (RemoteException | NullPointerException ex) {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the Status of Wi-Fi calling (Voice over WiFi) for the subscription ID specified.
+     * @param subId the subscription ID.
+     * @return true if VoWiFi is available, or false if it is unavailable or unknown.
      * @hide
      */
     public boolean isWifiCallingAvailable() {
        try {
-           return getITelephony().isWifiCallingAvailable();
-       } catch (RemoteException ex) {
-           return false;
-       } catch (NullPointerException ex) {
+           return getITelephony().isWifiCallingAvailable(getSubId());
+       } catch (RemoteException | NullPointerException ex) {
            return false;
        }
    }
 
+    /**
+     * The technology that IMS is registered for for the MMTEL feature.
+     * @param subId subscription ID to get IMS registration technology for.
+     * @return The IMS registration technology that IMS is registered to for the MMTEL feature.
+     * Valid return results are:
+     *  - {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE} for LTE registration,
+     *  - {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} for IWLAN registration, or
+     *  - {@link ImsRegistrationImplBase#REGISTRATION_TECH_NONE} if we are not registered or the
+     *  result is unavailable.
+     *  @hide
+     */
+    public @ImsRegistrationImplBase.ImsRegistrationTech int getImsRegTechnologyForMmTel() {
+        try {
+            return getITelephony().getImsRegTechnologyForMmTel(getSubId());
+        } catch (RemoteException ex) {
+            return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+        }
+    }
+
    /**
     * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone.
     *
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index bfdd453..1fdbae9 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -80,7 +80,7 @@
     public static final String EXTRA_PHONE_ID = "android:phone_id";
 
     /**
-     * Invalid feature value\
+     * Invalid feature value
      * @hide
      */
     public static final int FEATURE_INVALID = -1;
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2b4c059..02cc82c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1123,33 +1123,33 @@
     boolean isHearingAidCompatibilitySupported();
 
     /**
-     * Get IMS Registration Status
-     */
-    boolean isImsRegistered();
-
-    /**
      * Get IMS Registration Status on a particular subid.
      *
      * @param subId user preferred subId.
      *
      * @return {@code true} if the IMS status is registered.
      */
-    boolean isImsRegisteredForSubscriber(int subId);
+    boolean isImsRegistered(int subId);
 
     /**
-     * Returns the Status of Wi-Fi Calling
+     * Returns the Status of Wi-Fi Calling for the subscription id specified.
      */
-    boolean isWifiCallingAvailable();
+    boolean isWifiCallingAvailable(int subId);
 
     /**
-     * Returns the Status of Volte
+     * Returns the Status of VoLTE for the subscription ID specified.
      */
-    boolean isVolteAvailable();
+    boolean isVolteAvailable(int subId);
 
      /**
-     * Returns the Status of VT (video telephony)
+     * Returns the Status of VT (video telephony) for the subscription ID specified.
      */
-    boolean isVideoTelephonyAvailable();
+    boolean isVideoTelephonyAvailable(int subId);
+
+    /**
+    * Returns the MMTEL IMS registration technology for the subsciption ID specified.
+    */
+    int getImsRegTechnologyForMmTel(int subId);
 
     /**
       * Returns the unique device ID of phone, for example, the IMEI for
diff --git a/tests/ActivityManagerPerfTests/README.txt b/tests/ActivityManagerPerfTests/README.txt
index 77e0e90..1040ed1 100644
--- a/tests/ActivityManagerPerfTests/README.txt
+++ b/tests/ActivityManagerPerfTests/README.txt
@@ -1,12 +1,15 @@
 ActivityManagerPerfTests
 
 Performance tests for various ActivityManager components, e.g. Services, Broadcasts
+* These are only for tests that don't require a target package to test against
+* Self-contained perf tests should go in frameworks/base/apct-tests/perftests
 
-Command to run tests (not working yet, atest seems buggy)
-* atest .../frameworks/base/tests/ActivityManagerPerfTests
+Command to run tests
+* atest .../frameworks/base/tests/ActivityManagerPerfTests/tests/
+  * Command currently not working: b/71859981
 * m ActivityManagerPerfTests ActivityManagerPerfTestsTestApp && \
-  adb install $OUT/data/app/ActivityManagerPerfTests/ActivityManagerPerfTests.apk && \
-  adb install $OUT/data/app/ActivityManagerPerfTestsTestApp/ActivityManagerPerfTestsTestApp.apk && \
+  adb install "$OUT"/data/app/ActivityManagerPerfTests/ActivityManagerPerfTests.apk && \
+  adb install "$OUT"/data/app/ActivityManagerPerfTestsTestApp/ActivityManagerPerfTestsTestApp.apk && \
   adb shell am instrument -w \
   com.android.frameworks.perftests.amtests/android.support.test.runner.AndroidJUnitRunner
 
@@ -15,20 +18,42 @@
   * For example, the time it takes from sending an Intent to start a Service
     to the time the Service runs its callbacks
 * System.nanoTime() is monotonic and consistent between processes, so we use that for measuring time
-* To make sure the test app is running, we start an Activity
 * If the test app is involved, it will measure the time and send it back to the instrumentation test
-  * The time is sent back through a Binder interface in the Intent
+  * The time is sent back through a Binder interface in the Intent with the help of Utils.sendTime()
   * Each sent time is tagged with an id since there can be multiple events that send back a time
-    * For example, one is sent when the Activity is started, and another could be sent when a
-      Broadcast is received
+* Each test will run multiple times to account for variation in test runs
 
 Structure
 * tests
   * Instrumentation test which runs the various performance tests and reports the results
-
 * test-app
   * Target package which contains the Services, BroadcastReceivers, etc. to test against
   * Sends the time it measures back to the test package
-
 * utils
   * Utilities that both the instrumentation test and test app can use
+
+Adding tests
+* Example
+  * Look at tests/src/com/android/frameworks/perftests/am/BroadcastPerfTest and
+    test-app/src/com/android/frameworks/perftests/amteststestapp/TestBroadcastReceiver
+    for simple examples using this framework
+* Steps
+  * Add any components you will test against in the target package under
+    test-app/src/com/android/frameworks/perftests/amteststestapp/
+  * Add the test class under tests/src/com/android/frameworks/perftests/am/tests/
+    * The class should extend BasePerfTest
+    * Each test should call runPerfFunction() returning the elapsed time for a single iteration
+    * The test has access to a Context through mContext
+  * If you are measuring the time elapsed of something that either starts or ends in the target
+    package
+    * The target package can report the time it measures through an ITimeReceiverCallback passed
+      through an Intent through Utils.sendTime(intent, "tag")
+      (or however a Binder needs to be passed to the target package)
+    * The instrumentation test can collect that time by calling getReceivedTimeNs("tag") and
+      calculate the elapsed time
+    * Each timestamp sent to the instrumentation test is tagged with a tag since multiple timestamps
+      can be reported in an iteration
+  * If the target package should be running before your test logic starts, add startTargetPackage();
+    at the beginning of the iteration
+* Reporting
+  * Look at go/am-perf for how to add new tests to dashboards and receive notification on regression
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index cc792cc..03a617c 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -38,6 +38,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.timeout;
@@ -217,7 +218,8 @@
 
         // callback triggers
         captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_AVAILABLE));
-        verify(callback, timeout(500).times(1)).onAvailable(any());
+        verify(callback, timeout(500).times(1)).onAvailable(any(Network.class),
+                any(NetworkCapabilities.class), any(LinkProperties.class));
 
         // unregister callback
         manager.unregisterNetworkCallback(callback);
@@ -244,7 +246,8 @@
 
         // callback triggers
         captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_AVAILABLE));
-        verify(callback, timeout(100).times(1)).onAvailable(any());
+        verify(callback, timeout(100).times(1)).onAvailable(any(Network.class),
+                any(NetworkCapabilities.class), any(LinkProperties.class));
 
         // unregister callback
         manager.unregisterNetworkCallback(callback);
@@ -335,6 +338,10 @@
     static Message makeMessage(NetworkRequest req, int messageType) {
         Bundle bundle = new Bundle();
         bundle.putParcelable(NetworkRequest.class.getSimpleName(), req);
+        // Pass default objects as we don't care which get passed here
+        bundle.putParcelable(Network.class.getSimpleName(), new Network(1));
+        bundle.putParcelable(NetworkCapabilities.class.getSimpleName(), new NetworkCapabilities());
+        bundle.putParcelable(LinkProperties.class.getSimpleName(), new LinkProperties());
         Message msg = Message.obtain();
         msg.what = messageType;
         msg.setData(bundle);
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index f6c5532..f186ee5 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -17,6 +17,7 @@
 package android.net;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
@@ -48,18 +49,12 @@
         assertEquals(IpSecManager.INVALID_RESOURCE_ID, c.getSpiResourceId());
     }
 
-    @Test
-    public void testParcelUnparcel() throws Exception {
-        assertParcelingIsLossless(new IpSecConfig());
-
+    private IpSecConfig getSampleConfig() {
         IpSecConfig c = new IpSecConfig();
         c.setMode(IpSecTransform.MODE_TUNNEL);
         c.setSourceAddress("0.0.0.0");
         c.setDestinationAddress("1.2.3.4");
-        c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP);
-        c.setEncapSocketResourceId(7);
-        c.setEncapRemotePort(22);
-        c.setNattKeepaliveInterval(42);
+        c.setSpiResourceId(1984);
         c.setEncryption(
                 new IpSecAlgorithm(
                         IpSecAlgorithm.CRYPT_AES_CBC,
@@ -68,7 +63,37 @@
                 new IpSecAlgorithm(
                         IpSecAlgorithm.AUTH_HMAC_MD5,
                         new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}));
-        c.setSpiResourceId(1984);
+        c.setAuthenticatedEncryption(
+                new IpSecAlgorithm(
+                        IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+                        new byte[] {
+                            1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0, 1, 2, 3, 4
+                        },
+                        128));
+        c.setEncapType(android.system.OsConstants.UDP_ENCAP_ESPINUDP);
+        c.setEncapSocketResourceId(7);
+        c.setEncapRemotePort(22);
+        c.setNattKeepaliveInterval(42);
+        c.setMarkValue(12);
+        c.setMarkMask(23);
+
+        return c;
+    }
+
+    @Test
+    public void testCopyConstructor() {
+        IpSecConfig original = getSampleConfig();
+        IpSecConfig copy = new IpSecConfig(original);
+
+        assertTrue(IpSecConfig.equals(original, copy));
+        assertFalse(original == copy);
+    }
+
+    @Test
+    public void testParcelUnparcel() throws Exception {
+        assertParcelingIsLossless(new IpSecConfig());
+
+        IpSecConfig c = getSampleConfig();
         assertParcelingIsLossless(c);
     }
 
diff --git a/tests/net/java/android/net/IpSecTransformTest.java b/tests/net/java/android/net/IpSecTransformTest.java
new file mode 100644
index 0000000..b4342df
--- /dev/null
+++ b/tests/net/java/android/net/IpSecTransformTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 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.net;
+
+import static org.junit.Assert.assertFalse;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Unit tests for {@link IpSecTransform}. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class IpSecTransformTest {
+
+    @Test
+    public void testCreateTransformCopiesConfig() {
+        // Create a config with a few parameters to make sure it's not empty
+        IpSecConfig config = new IpSecConfig();
+        config.setSourceAddress("0.0.0.0");
+        config.setDestinationAddress("1.2.3.4");
+        config.setSpiResourceId(1984);
+
+        IpSecTransform preModification = new IpSecTransform(null, config);
+
+        config.setSpiResourceId(1985);
+        IpSecTransform postModification = new IpSecTransform(null, config);
+
+        assertFalse(IpSecTransform.equals(preModification, postModification));
+    }
+
+    @Test
+    public void testCreateTransformsWithSameConfigEqual() {
+        // Create a config with a few parameters to make sure it's not empty
+        IpSecConfig config = new IpSecConfig();
+        config.setSourceAddress("0.0.0.0");
+        config.setDestinationAddress("1.2.3.4");
+        config.setSpiResourceId(1984);
+
+        IpSecTransform config1 = new IpSecTransform(null, config);
+        IpSecTransform config2 = new IpSecTransform(null, config);
+
+        assertFalse(IpSecTransform.equals(config1, config2));
+    }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index e7abede..24639e9 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -35,6 +35,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL;
@@ -528,6 +529,11 @@
             mNetworkAgent.sendNetworkInfo(mNetworkInfo);
         }
 
+        public void resume() {
+            mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
+            mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+        }
+
         public void disconnect() {
             mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
             mNetworkAgent.sendNetworkInfo(mNetworkInfo);
@@ -569,6 +575,10 @@
             assertTrue(mNetworkStatusReceived.block(TIMEOUT_MS));
             return mRedirectUrl;
         }
+
+        public NetworkCapabilities getNetworkCapabilities() {
+            return mNetworkCapabilities;
+        }
     }
 
     /**
@@ -1273,6 +1283,7 @@
         NETWORK_CAPABILITIES,
         LINK_PROPERTIES,
         SUSPENDED,
+        RESUMED,
         LOSING,
         LOST,
         UNAVAILABLE
@@ -1344,6 +1355,11 @@
         }
 
         @Override
+        public void onNetworkResumed(Network network) {
+            setLastCallback(CallbackState.RESUMED, network, null);
+        }
+
+        @Override
         public void onLosing(Network network, int maxMsToLive) {
             setLastCallback(CallbackState.LOSING, network, maxMsToLive /* autoboxed int */);
         }
@@ -2459,16 +2475,31 @@
 
         // Suspend the network.
         mCellNetworkAgent.suspend();
+        cellNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_SUSPENDED,
+                mCellNetworkAgent);
         cellNetworkCallback.expectCallback(CallbackState.SUSPENDED, mCellNetworkAgent);
         cellNetworkCallback.assertNoCallback();
 
         // Register a garden variety default network request.
-        final TestNetworkCallback dfltNetworkCallback = new TestNetworkCallback();
+        TestNetworkCallback dfltNetworkCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(dfltNetworkCallback);
         // We should get onAvailable(), onCapabilitiesChanged(), onLinkPropertiesChanged(),
         // as well as onNetworkSuspended() in rapid succession.
         dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent, true);
         dfltNetworkCallback.assertNoCallback();
+        mCm.unregisterNetworkCallback(dfltNetworkCallback);
+
+        mCellNetworkAgent.resume();
+        cellNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_NOT_SUSPENDED,
+                mCellNetworkAgent);
+        cellNetworkCallback.expectCallback(CallbackState.RESUMED, mCellNetworkAgent);
+        cellNetworkCallback.assertNoCallback();
+
+        dfltNetworkCallback = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallback(dfltNetworkCallback);
+        // This time onNetworkSuspended should not be called.
+        dfltNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
+        dfltNetworkCallback.assertNoCallback();
 
         mCm.unregisterNetworkCallback(dfltNetworkCallback);
         mCm.unregisterNetworkCallback(cellNetworkCallback);
@@ -3682,8 +3713,7 @@
         vpnNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
 
         genericNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
-        vpnNetworkCallback.expectCapabilitiesLike(
-                nc -> nc.appliesToUid(uid) && !nc.appliesToUid(uid + 1), vpnNetworkAgent);
+        vpnNetworkCallback.expectCapabilitiesLike(nc -> null == nc.getUids(), vpnNetworkAgent);
 
         ranges.clear();
         vpnNetworkAgent.setUids(ranges);
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 5831875..f064cb1 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -414,59 +414,78 @@
  public:
   using xml::ConstVisitor::Visit;
 
+  XmlPrinter(Printer* printer) : printer_(printer) {
+  }
+
   void Visit(const xml::Element* el) override {
-    const size_t previous_size = prefix_.size();
-
     for (const xml::NamespaceDecl& decl : el->namespace_decls) {
-      std::cerr << prefix_ << "N: " << decl.prefix << "=" << decl.uri
-                << " (line=" << decl.line_number << ")\n";
-      prefix_ += "  ";
+      printer_->Println(StringPrintf("N: %s=%s (line=%zu)", decl.prefix.c_str(), decl.uri.c_str(),
+                                     decl.line_number));
+      printer_->Indent();
     }
 
-    std::cerr << prefix_ << "E: ";
+    printer_->Print("E: ");
     if (!el->namespace_uri.empty()) {
-      std::cerr << el->namespace_uri << ":";
+      printer_->Print(el->namespace_uri);
+      printer_->Print(":");
     }
-    std::cerr << el->name << " (line=" << el->line_number << ")\n";
+    printer_->Println(StringPrintf("%s (line=%zu)", el->name.c_str(), el->line_number));
+    printer_->Indent();
 
     for (const xml::Attribute& attr : el->attributes) {
-      std::cerr << prefix_ << "  A: ";
+      printer_->Print("A: ");
       if (!attr.namespace_uri.empty()) {
-        std::cerr << attr.namespace_uri << ":";
+        printer_->Print(attr.namespace_uri);
+        printer_->Print(":");
       }
-      std::cerr << attr.name;
+      printer_->Print(attr.name);
 
       if (attr.compiled_attribute) {
-        std::cerr << "(" << attr.compiled_attribute.value().id.value_or_default(ResourceId(0x0))
-                  << ")";
+        printer_->Print("(");
+        printer_->Print(
+            attr.compiled_attribute.value().id.value_or_default(ResourceId(0)).to_string());
+        printer_->Print(")");
       }
-      std::cerr << "=";
+      printer_->Print("=");
       if (attr.compiled_value != nullptr) {
-        std::cerr << *attr.compiled_value;
+        attr.compiled_value->PrettyPrint(printer_);
       } else {
-        std::cerr << attr.value;
+        printer_->Print("\"");
+        printer_->Print(attr.value);
+        printer_->Print("\"");
       }
-      std::cerr << "\n";
+
+      if (!attr.value.empty()) {
+        printer_->Print(" (Raw: \"");
+        printer_->Print(attr.value);
+        printer_->Print("\")");
+      }
+      printer_->Println();
     }
 
-    prefix_ += "  ";
+    printer_->Indent();
     xml::ConstVisitor::Visit(el);
-    prefix_.resize(previous_size);
+    printer_->Undent();
+    printer_->Undent();
+
+    for (size_t i = 0; i < el->namespace_decls.size(); i++) {
+      printer_->Undent();
+    }
   }
 
   void Visit(const xml::Text* text) override {
-    std::cerr << prefix_ << "T: '" << text->text << "'\n";
+    printer_->Println(StringPrintf("T: '%s'", text->text.c_str()));
   }
 
  private:
-  std::string prefix_;
+  Printer* printer_;
 };
 
 }  // namespace
 
-void Debug::DumpXml(const xml::XmlResource& doc) {
-  XmlPrinter printer;
-  doc.root->Accept(&printer);
+void Debug::DumpXml(const xml::XmlResource& doc, Printer* printer) {
+  XmlPrinter xml_visitor(printer);
+  doc.root->Accept(&xml_visitor);
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index 6209a04..382707e 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -37,7 +37,7 @@
                          text::Printer* printer);
   static void PrintStyleGraph(ResourceTable* table, const ResourceName& target_style);
   static void DumpHex(const void* data, size_t len);
-  static void DumpXml(const xml::XmlResource& doc);
+  static void DumpXml(const xml::XmlResource& doc, text::Printer* printer);
 };
 
 }  // namespace aapt
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 20a9f41..ac28227 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -222,7 +222,9 @@
 
     } else if (manifest != nullptr && path == "AndroidManifest.xml") {
       BigBuffer buffer(8192);
-      XmlFlattener xml_flattener(&buffer, {});
+      XmlFlattenerOptions xml_flattener_options;
+      xml_flattener_options.use_utf16 = true;
+      XmlFlattener xml_flattener(&buffer, xml_flattener_options);
       if (!xml_flattener.Consume(context, manifest)) {
         context->GetDiagnostics()->Error(DiagMessage(path) << "flattening failed");
         return false;
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 02ac86c..628466d 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -520,6 +520,10 @@
   return util::make_unique<BinaryPrimitive>(value);
 }
 
+std::unique_ptr<BinaryPrimitive> MakeInt(uint32_t val) {
+  return util::make_unique<BinaryPrimitive>(android::Res_value::TYPE_INT_DEC, val);
+}
+
 std::unique_ptr<BinaryPrimitive> TryParseFloat(const StringPiece& str) {
   std::u16string str16 = util::Utf8ToUtf16(util::TrimWhitespace(str));
   android::Res_value value;
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index 36f6c2b..f83d49e 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -165,6 +165,9 @@
  */
 std::unique_ptr<BinaryPrimitive> TryParseInt(const android::StringPiece& str);
 
+// Returns an integer BinaryPrimitive.
+std::unique_ptr<BinaryPrimitive> MakeInt(uint32_t value);
+
 /*
  * Returns a BinaryPrimitve object representing a floating point number
  * (float, dimension, etc) if the string was parsed as one.
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index d80307c..7f956c5 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -139,6 +139,7 @@
     BigBuffer buffer(4096);
     XmlFlattenerOptions options = {};
     options.use_utf16 = utf16;
+    options.keep_raw_values = true;
     XmlFlattener flattener(&buffer, options);
     if (!flattener.Consume(context_, xml)) {
       return false;
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index 3d2fb55..8e7e5e5 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -38,6 +38,13 @@
 
 namespace aapt {
 
+struct DumpOptions {
+  DebugPrintTableOptions print_options;
+
+  // The path to a file within an APK to dump.
+  Maybe<std::string> file_to_dump_path;
+};
+
 static const char* ResourceFileTypeToString(const ResourceFile::Type& type) {
   switch (type) {
     case ResourceFile::Type::kPng:
@@ -69,8 +76,52 @@
   printer->Println(StringPrintf("Data:     offset=%" PRIi64 " length=%zd", offset, len));
 }
 
+static bool DumpXmlFile(IAaptContext* context, io::IFile* file, bool proto,
+                        text::Printer* printer) {
+  std::unique_ptr<xml::XmlResource> doc;
+  if (proto) {
+    std::unique_ptr<io::InputStream> in = file->OpenInputStream();
+    if (in == nullptr) {
+      context->GetDiagnostics()->Error(DiagMessage() << "failed to open file");
+      return false;
+    }
+
+    io::ZeroCopyInputAdaptor adaptor(in.get());
+    pb::XmlNode pb_node;
+    if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
+      context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as proto XML");
+      return false;
+    }
+
+    std::string err;
+    doc = DeserializeXmlResourceFromPb(pb_node, &err);
+    if (doc == nullptr) {
+      context->GetDiagnostics()->Error(DiagMessage() << "failed to deserialize proto XML");
+      return false;
+    }
+    printer->Println("Proto XML");
+  } else {
+    std::unique_ptr<io::IData> data = file->OpenAsData();
+    if (data == nullptr) {
+      context->GetDiagnostics()->Error(DiagMessage() << "failed to open file");
+      return false;
+    }
+
+    std::string err;
+    doc = xml::Inflate(data->data(), data->size(), &err);
+    if (doc == nullptr) {
+      context->GetDiagnostics()->Error(DiagMessage() << "failed to parse file as binary XML");
+      return false;
+    }
+    printer->Println("Binary XML");
+  }
+
+  Debug::DumpXml(*doc, printer);
+  return true;
+}
+
 static bool TryDumpFile(IAaptContext* context, const std::string& file_path,
-                        const DebugPrintTableOptions& print_options) {
+                        const DumpOptions& options) {
   // Use a smaller buffer so that there is less latency for dumping to stdout.
   constexpr size_t kStdOutBufferSize = 1024u;
   io::FileOutputStream fout(STDOUT_FILENO, kStdOutBufferSize);
@@ -80,7 +131,10 @@
   std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(file_path, &err);
   if (zip) {
     ResourceTable table;
+    bool proto = false;
     if (io::IFile* file = zip->FindFile("resources.pb")) {
+      proto = true;
+
       std::unique_ptr<io::IData> data = file->OpenAsData();
       if (data == nullptr) {
         context->GetDiagnostics()->Error(DiagMessage(file_path) << "failed to open resources.pb");
@@ -98,8 +152,6 @@
                                          << "failed to parse table: " << err);
         return false;
       }
-
-      printer.Println("Proto APK");
     } else if (io::IFile* file = zip->FindFile("resources.arsc")) {
       std::unique_ptr<io::IData> data = file->OpenAsData();
       if (!data) {
@@ -112,12 +164,26 @@
       if (!parser.Parse()) {
         return false;
       }
-
-      printer.Println("Binary APK");
     }
 
-    Debug::PrintTable(table, print_options, &printer);
-    return true;
+    if (!options.file_to_dump_path) {
+      if (proto) {
+        printer.Println("Proto APK");
+      } else {
+        printer.Println("Binary APK");
+      }
+      Debug::PrintTable(table, options.print_options, &printer);
+      return true;
+    }
+
+    io::IFile* file = zip->FindFile(options.file_to_dump_path.value());
+    if (file == nullptr) {
+      context->GetDiagnostics()->Error(DiagMessage(file_path)
+                                       << "file '" << options.file_to_dump_path.value()
+                                       << "' not found in APK");
+      return false;
+    }
+    return DumpXmlFile(context, file, proto, &printer);
   }
 
   err.clear();
@@ -159,7 +225,7 @@
       }
 
       printer.Indent();
-      Debug::PrintTable(table, print_options, &printer);
+      Debug::PrintTable(table, options.print_options, &printer);
       printer.Undent();
     } else if (entry->Type() == ContainerEntryType::kResFile) {
       printer.Println("kResFile");
@@ -243,10 +309,13 @@
 int Dump(const std::vector<StringPiece>& args) {
   bool verbose = false;
   bool no_values = false;
+  DumpOptions options;
   Flags flags = Flags()
                     .OptionalSwitch("--no-values",
                                     "Suppresses output of values when displaying resource tables.",
                                     &no_values)
+                    .OptionalFlag("--file", "Dumps the specified file from the APK passed as arg.",
+                                  &options.file_to_dump_path)
                     .OptionalSwitch("-v", "increase verbosity of output", &verbose);
   if (!flags.Parse("aapt2 dump", args, &std::cerr)) {
     return 1;
@@ -255,11 +324,10 @@
   DumpContext context;
   context.SetVerbose(verbose);
 
-  DebugPrintTableOptions dump_table_options;
-  dump_table_options.show_sources = true;
-  dump_table_options.show_values = !no_values;
+  options.print_options.show_sources = true;
+  options.print_options.show_values = !no_values;
   for (const std::string& arg : flags.GetArgs()) {
-    if (!TryDumpFile(&context, arg, dump_table_options)) {
+    if (!TryDumpFile(&context, arg, options)) {
       return 1;
     }
   }
diff --git a/tools/aapt2/configuration/ConfigurationParser.cpp b/tools/aapt2/configuration/ConfigurationParser.cpp
index eabeb47..902334b 100644
--- a/tools/aapt2/configuration/ConfigurationParser.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser.cpp
@@ -20,6 +20,7 @@
 #include <functional>
 #include <map>
 #include <memory>
+#include <string>
 #include <utility>
 
 #include "android-base/file.h"
@@ -93,6 +94,7 @@
 };
 NoopDiagnostics noop_;
 
+/** Returns the value of the label attribute for a given element. */
 std::string GetLabel(const Element* element, IDiagnostics* diag) {
   std::string label;
   for (const auto& attr : element->attributes) {
@@ -108,6 +110,18 @@
   return label;
 }
 
+/** Returns the value of the version-code-order attribute for a given element. */
+Maybe<int32_t> GetVersionCodeOrder(const Element* element, IDiagnostics* diag) {
+  const xml::Attribute* version = element->FindAttribute("", "version-code-order");
+  if (version == nullptr) {
+    std::string label = GetLabel(element, diag);
+    diag->Error(DiagMessage() << "No version-code-order found for element '" << element->name
+                              << "' with label '" << label << "'");
+    return {};
+  }
+  return std::stoi(version->value);
+}
+
 /** XML node visitor that removes all of the namespace URIs from the node and all children. */
 class NamespaceVisitor : public xml::Visitor {
  public:
@@ -437,26 +451,37 @@
   // Convert from a parsed configuration to a list of artifacts for processing.
   const std::string& apk_name = file::GetFilename(apk_path).to_string();
   std::vector<OutputArtifact> output_artifacts;
-  bool has_errors = false;
 
   PostProcessingConfiguration& config = maybe_config.value();
-  config.SortArtifacts();
 
+  bool valid = true;
   int version = 1;
+
   for (const ConfiguredArtifact& artifact : config.artifacts) {
     Maybe<OutputArtifact> output_artifact = ToOutputArtifact(artifact, apk_name, config, diag_);
     if (!output_artifact) {
       // Defer return an error condition so that all errors are reported.
-      has_errors = true;
+      valid = false;
     } else {
       output_artifact.value().version = version++;
       output_artifacts.push_back(std::move(output_artifact.value()));
     }
   }
 
-  if (has_errors) {
+  if (!config.ValidateVersionCodeOrdering(diag_)) {
+    diag_->Error(DiagMessage() << "could not validate post processing configuration");
+    valid = false;
+  }
+
+  if (valid) {
+    // Sorting artifacts requires that all references are valid as it uses them to determine order.
+    config.SortArtifacts();
+  }
+
+  if (!valid) {
     return {};
   }
+
   return {output_artifacts};
 }
 
@@ -509,8 +534,15 @@
     return false;
   }
 
-  auto& group = GetOrCreateGroup(label, &config->abi_groups);
   bool valid = true;
+  OrderedEntry<Abi>& entry = config->abi_groups[label];
+  Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+  if (!order) {
+    valid = false;
+  } else {
+    entry.order = order.value();
+  }
+  auto& group = entry.entry;
 
   // Special case for empty abi-group tag. Label will be used as the ABI.
   if (root_element->GetChildElements().empty()) {
@@ -519,7 +551,7 @@
       return false;
     }
     group.push_back(abi->second);
-    return true;
+    return valid;
   }
 
   for (auto* child : root_element->GetChildElements()) {
@@ -553,8 +585,15 @@
     return false;
   }
 
-  auto& group = GetOrCreateGroup(label, &config->screen_density_groups);
   bool valid = true;
+  OrderedEntry<ConfigDescription>& entry = config->screen_density_groups[label];
+  Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+  if (!order) {
+    valid = false;
+  } else {
+    entry.order = order.value();
+  }
+  auto& group = entry.entry;
 
   // Special case for empty screen-density-group tag. Label will be used as the screen density.
   if (root_element->GetChildElements().empty()) {
@@ -613,8 +652,15 @@
     return false;
   }
 
-  auto& group = GetOrCreateGroup(label, &config->locale_groups);
   bool valid = true;
+  OrderedEntry<ConfigDescription>& entry = config->locale_groups[label];
+  Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+  if (!order) {
+    valid = false;
+  } else {
+    entry.order = order.value();
+  }
+  auto& group = entry.entry;
 
   // Special case to auto insert a locale for an empty group. Label will be used for locale.
   if (root_element->GetChildElements().empty()) {
@@ -728,8 +774,15 @@
     return false;
   }
 
-  auto& group = GetOrCreateGroup(label, &config->gl_texture_groups);
   bool valid = true;
+  OrderedEntry<GlTexture>& entry = config->gl_texture_groups[label];
+  Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+  if (!order) {
+    valid = false;
+  } else {
+    entry.order = order.value();
+  }
+  auto& group = entry.entry;
 
   GlTexture result;
   for (auto* child : root_element->GetChildElements()) {
@@ -771,8 +824,15 @@
     return false;
   }
 
-  auto& group = GetOrCreateGroup(label, &config->device_feature_groups);
   bool valid = true;
+  OrderedEntry<DeviceFeature>& entry = config->device_feature_groups[label];
+  Maybe<int32_t> order = GetVersionCodeOrder(root_element, diag);
+  if (!order) {
+    valid = false;
+  } else {
+    entry.order = order.value();
+  }
+  auto& group = entry.entry;
 
   for (auto* child : root_element->GetChildElements()) {
     if (child->name != "supports-feature") {
diff --git a/tools/aapt2/configuration/ConfigurationParser.internal.h b/tools/aapt2/configuration/ConfigurationParser.internal.h
index a583057..f071a69 100644
--- a/tools/aapt2/configuration/ConfigurationParser.internal.h
+++ b/tools/aapt2/configuration/ConfigurationParser.internal.h
@@ -33,18 +33,31 @@
 
 template <typename T>
 struct OrderedEntry {
-  size_t order;
+  int32_t order;
   std::vector<T> entry;
 };
 
-/** A mapping of group labels to group of configuration items. */
-template <class T>
-using Group = std::unordered_map<std::string, OrderedEntry<T>>;
-
 /** A mapping of group label to a single configuration item. */
 template <class T>
 using Entry = std::unordered_map<std::string, T>;
 
+/** A mapping of group labels to group of configuration items. */
+template <class T>
+using Group = Entry<OrderedEntry<T>>;
+
+template<typename T>
+bool IsGroupValid(const Group<T>& group, const std::string& name, IDiagnostics* diag) {
+  std::set<int32_t> orders;
+  for (const auto& p : group) {
+    orders.insert(p.second.order);
+  }
+  bool valid = orders.size() == group.size();
+  if (!valid) {
+    diag->Error(DiagMessage() << name << " have overlapping version-code-order attributes");
+  }
+  return valid;
+}
+
 /** Retrieves an entry from the provided Group, creating a new instance if one does not exist. */
 template <typename T>
 std::vector<T>& GetOrCreateGroup(std::string label, Group<T>* group) {
@@ -93,7 +106,7 @@
 
  private:
   template <typename T>
-  inline size_t GetGroupOrder(const Group<T>& groups, const Maybe<std::string>& label) {
+  inline size_t GetGroupOrder(const Entry<T>& groups, const Maybe<std::string>& label) {
     if (!label) {
       return std::numeric_limits<size_t>::max();
     }
@@ -141,6 +154,15 @@
   Group<GlTexture> gl_texture_groups;
   Entry<AndroidSdk> android_sdks;
 
+  bool ValidateVersionCodeOrdering(IDiagnostics* diag) {
+    bool valid = IsGroupValid(abi_groups, "abi-groups", diag);
+    valid &= IsGroupValid(screen_density_groups, "screen-density-groups", diag);
+    valid &= IsGroupValid(locale_groups, "locale-groups", diag);
+    valid &= IsGroupValid(device_feature_groups, "device-feature-groups", diag);
+    valid &= IsGroupValid(gl_texture_groups, "gl-texture-groups", diag);
+    return valid;
+  }
+
   /**
    * Sorts the configured artifacts based on the ordering of the groups in the configuration file.
    * The only exception to this rule is Android SDK versions. Larger SDK versions will have a larger
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index 0329846..febbb2e 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -82,22 +82,22 @@
 constexpr const char* kValidConfig = R"(<?xml version="1.0" encoding="utf-8" ?>
 <post-process xmlns="http://schemas.android.com/tools/aapt">
   <abi-groups>
-    <abi-group label="arm">
-      <abi>armeabi-v7a</abi>
-      <abi>arm64-v8a</abi>
-    </abi-group>
-    <abi-group label="other">
+    <abi-group label="other" version-code-order="2">
       <abi>x86</abi>
       <abi>mips</abi>
     </abi-group>
+    <abi-group label="arm" version-code-order="1">
+      <abi>armeabi-v7a</abi>
+      <abi>arm64-v8a</abi>
+    </abi-group>
   </abi-groups>
   <screen-density-groups>
-    <screen-density-group label="large">
+    <screen-density-group label="large" version-code-order="2">
       <screen-density>xhdpi</screen-density>
       <screen-density>xxhdpi</screen-density>
       <screen-density>xxxhdpi</screen-density>
     </screen-density-group>
-    <screen-density-group label="alldpi">
+    <screen-density-group label="alldpi" version-code-order="1">
       <screen-density>ldpi</screen-density>
       <screen-density>mdpi</screen-density>
       <screen-density>hdpi</screen-density>
@@ -107,17 +107,20 @@
     </screen-density-group>
   </screen-density-groups>
   <locale-groups>
-    <locale-group label="europe">
+    <locale-group label="europe" version-code-order="1">
       <locale>en</locale>
       <locale>es</locale>
       <locale>fr</locale>
       <locale>de</locale>
     </locale-group>
-    <locale-group label="north-america">
+    <locale-group label="north-america" version-code-order="2">
       <locale>en</locale>
       <locale>es-rMX</locale>
       <locale>fr-rCA</locale>
     </locale-group>
+    <locale-group label="all" version-code-order="-1">
+      <locale />
+    </locale-group>
   </locale-groups>
   <android-sdks>
     <android-sdk
@@ -131,14 +134,14 @@
     </android-sdk>
   </android-sdks>
   <gl-texture-groups>
-    <gl-texture-group label="dxt1">
+    <gl-texture-group label="dxt1" version-code-order="2">
       <gl-texture name="GL_EXT_texture_compression_dxt1">
         <texture-path>assets/dxt1/*</texture-path>
       </gl-texture>
     </gl-texture-group>
   </gl-texture-groups>
   <device-feature-groups>
-    <device-feature-group label="low-latency">
+    <device-feature-group label="low-latency" version-code-order="2">
       <supports-feature>android.hardware.audio.low_latency</supports-feature>
     </device-feature-group>
   </device-feature-groups>
@@ -188,19 +191,22 @@
 
   auto& arm = config.abi_groups["arm"];
   auto& other = config.abi_groups["other"];
-  EXPECT_EQ(arm.order, 1ul);
-  EXPECT_EQ(other.order, 2ul);
+  EXPECT_EQ(arm.order, 1);
+  EXPECT_EQ(other.order, 2);
 
   auto& large = config.screen_density_groups["large"];
   auto& alldpi = config.screen_density_groups["alldpi"];
-  EXPECT_EQ(large.order, 1ul);
-  EXPECT_EQ(alldpi.order, 2ul);
+  EXPECT_EQ(large.order, 2);
+  EXPECT_EQ(alldpi.order, 1);
 
   auto& north_america = config.locale_groups["north-america"];
   auto& europe = config.locale_groups["europe"];
+  auto& all = config.locale_groups["all"];
   // Checked in reverse to make sure access order does not matter.
-  EXPECT_EQ(north_america.order, 2ul);
-  EXPECT_EQ(europe.order, 1ul);
+  EXPECT_EQ(north_america.order, 2);
+  EXPECT_EQ(europe.order, 1);
+  EXPECT_EQ(all.order, -1);
+  EXPECT_EQ(3ul, config.locale_groups.size());
 }
 
 TEST_F(ConfigurationParserTest, ValidateFile) {
@@ -392,7 +398,7 @@
 
 TEST_F(ConfigurationParserTest, AbiGroupAction) {
   static constexpr const char* xml = R"xml(
-    <abi-group label="arm">
+    <abi-group label="arm"  version-code-order="2">
       <!-- First comment. -->
       <abi>
         armeabi-v7a
@@ -415,7 +421,8 @@
 }
 
 TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup) {
-  static constexpr const char* xml = R"xml(<abi-group label="arm64-v8a"/>)xml";
+  static constexpr const char* xml =
+      R"xml(<abi-group label="arm64-v8a" version-code-order="3"/>)xml";
 
   auto doc = test::BuildXmlDom(xml);
 
@@ -426,12 +433,23 @@
   EXPECT_THAT(config.abi_groups, SizeIs(1ul));
   ASSERT_EQ(1u, config.abi_groups.count("arm64-v8a"));
 
-  auto& out = config.abi_groups["arm64-v8a"].entry;
-  ASSERT_THAT(out, ElementsAre(Abi::kArm64V8a));
+  auto& out = config.abi_groups["arm64-v8a"];
+  ASSERT_THAT(out.entry, ElementsAre(Abi::kArm64V8a));
+  EXPECT_EQ(3, out.order);
+}
+
+TEST_F(ConfigurationParserTest, AbiGroupAction_EmptyGroup_NoOrder) {
+  static constexpr const char* xml = R"xml(<abi-group label="arm64-v8a"/>)xml";
+
+  auto doc = test::BuildXmlDom(xml);
+
+  PostProcessingConfiguration config;
+  bool ok = AbiGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+  ASSERT_FALSE(ok);
 }
 
 TEST_F(ConfigurationParserTest, AbiGroupAction_InvalidEmptyGroup) {
-  static constexpr const char* xml = R"xml(<abi-group label="arm"/>)xml";
+  static constexpr const char* xml = R"xml(<abi-group label="arm" order="2"/>)xml";
 
   auto doc = test::BuildXmlDom(xml);
 
@@ -442,7 +460,7 @@
 
 TEST_F(ConfigurationParserTest, ScreenDensityGroupAction) {
   static constexpr const char* xml = R"xml(
-    <screen-density-group label="large">
+    <screen-density-group label="large" version-code-order="2">
       <screen-density>xhdpi</screen-density>
       <screen-density>
         xxhdpi
@@ -471,7 +489,8 @@
 }
 
 TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup) {
-  static constexpr const char* xml = R"xml(<screen-density-group label="xhdpi"/>)xml";
+  static constexpr const char* xml =
+      R"xml(<screen-density-group label="xhdpi" version-code-order="4"/>)xml";
 
   auto doc = test::BuildXmlDom(xml);
 
@@ -485,8 +504,19 @@
   ConfigDescription xhdpi;
   xhdpi.density = ResTable_config::DENSITY_XHIGH;
 
-  auto& out = config.screen_density_groups["xhdpi"].entry;
-  ASSERT_THAT(out, ElementsAre(xhdpi));
+  auto& out = config.screen_density_groups["xhdpi"];
+  EXPECT_THAT(out.entry, ElementsAre(xhdpi));
+  EXPECT_EQ(4, out.order);
+}
+
+TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_EmtpyGroup_NoVersion) {
+  static constexpr const char* xml = R"xml(<screen-density-group label="xhdpi"/>)xml";
+
+  auto doc = test::BuildXmlDom(xml);
+
+  PostProcessingConfiguration config;
+  bool ok = ScreenDensityGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+  ASSERT_FALSE(ok);
 }
 
 TEST_F(ConfigurationParserTest, ScreenDensityGroupAction_InvalidEmtpyGroup) {
@@ -501,7 +531,7 @@
 
 TEST_F(ConfigurationParserTest, LocaleGroupAction) {
   static constexpr const char* xml = R"xml(
-    <locale-group label="europe">
+    <locale-group label="europe" version-code-order="2">
       <locale>en</locale>
       <locale>es</locale>
       <locale>fr</locale>
@@ -528,7 +558,7 @@
 }
 
 TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup) {
-  static constexpr const char* xml = R"xml(<locale-group label="en"/>)xml";
+  static constexpr const char* xml = R"xml(<locale-group label="en" version-code-order="6"/>)xml";
 
   auto doc = test::BuildXmlDom(xml);
 
@@ -539,11 +569,22 @@
   ASSERT_EQ(1ul, config.locale_groups.size());
   ASSERT_EQ(1u, config.locale_groups.count("en"));
 
-  const auto& out = config.locale_groups["en"].entry;
+  const auto& out = config.locale_groups["en"];
 
   ConfigDescription en = test::ParseConfigOrDie("en");
 
-  ASSERT_THAT(out, ElementsAre(en));
+  EXPECT_THAT(out.entry, ElementsAre(en));
+  EXPECT_EQ(6, out.order);
+}
+
+TEST_F(ConfigurationParserTest, LocaleGroupAction_EmtpyGroup_NoOrder) {
+  static constexpr const char* xml = R"xml(<locale-group label="en"/>)xml";
+
+  auto doc = test::BuildXmlDom(xml);
+
+  PostProcessingConfiguration config;
+  bool ok = LocaleGroupTagHandler(&config, NodeCast<Element>(doc.get()->root.get()), &diag_);
+  ASSERT_FALSE(ok);
 }
 
 TEST_F(ConfigurationParserTest, LocaleGroupAction_InvalidEmtpyGroup) {
@@ -695,7 +736,7 @@
 
 TEST_F(ConfigurationParserTest, GlTextureGroupAction) {
   static constexpr const char* xml = R"xml(
-    <gl-texture-group label="dxt1">
+    <gl-texture-group label="dxt1" version-code-order="2">
       <gl-texture name="GL_EXT_texture_compression_dxt1">
         <texture-path>assets/dxt1/main/*</texture-path>
         <texture-path>
@@ -726,7 +767,7 @@
 
 TEST_F(ConfigurationParserTest, DeviceFeatureGroupAction) {
   static constexpr const char* xml = R"xml(
-    <device-feature-group label="low-latency">
+    <device-feature-group label="low-latency" version-code-order="2">
       <supports-feature>android.hardware.audio.low_latency</supports-feature>
       <supports-feature>
         android.hardware.audio.pro
@@ -749,6 +790,30 @@
   ASSERT_THAT(out, ElementsAre(low_latency, pro));
 }
 
+TEST_F(ConfigurationParserTest, Group_Valid) {
+  Group<int32_t> group;
+  group["item1"].order = 1;
+  group["item2"].order = 2;
+  group["item3"].order = 3;
+  group["item4"].order = 4;
+  group["item5"].order = 5;
+  group["item6"].order = 6;
+
+  EXPECT_TRUE(IsGroupValid(group, "test", &diag_));
+}
+
+TEST_F(ConfigurationParserTest, Group_OverlappingOrder) {
+  Group<int32_t> group;
+  group["item1"].order = 1;
+  group["item2"].order = 2;
+  group["item3"].order = 3;
+  group["item4"].order = 2;
+  group["item5"].order = 5;
+  group["item6"].order = 1;
+
+  EXPECT_FALSE(IsGroupValid(group, "test", &diag_));
+}
+
 // Artifact name parser test cases.
 
 TEST(ArtifactTest, Simple) {
diff --git a/tools/aapt2/configuration/aapt2.xsd b/tools/aapt2/configuration/aapt2.xsd
index fb2f49b..a28e28b 100644
--- a/tools/aapt2/configuration/aapt2.xsd
+++ b/tools/aapt2/configuration/aapt2.xsd
@@ -81,6 +81,7 @@
       <xsd:element name="gl-texture" type="gl-texture" maxOccurs="unbounded"/>
     </xsd:sequence>
     <xsd:attribute name="label" type="xsd:string"/>
+    <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/>
   </xsd:complexType>
 
   <xsd:complexType name="gl-texture">
@@ -95,6 +96,7 @@
       <xsd:element name="supports-feature" type="xsd:string" maxOccurs="unbounded"/>
     </xsd:sequence>
     <xsd:attribute name="label" type="xsd:string"/>
+    <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/>
   </xsd:complexType>
 
   <xsd:complexType name="abi-group">
@@ -102,6 +104,7 @@
       <xsd:element name="abi" type="abi-name" maxOccurs="unbounded"/>
     </xsd:sequence>
     <xsd:attribute name="label" type="xsd:string"/>
+    <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/>
   </xsd:complexType>
 
   <xsd:simpleType name="abi-name">
@@ -122,6 +125,7 @@
       <xsd:element name="screen-density" type="screen-density" maxOccurs="unbounded"/>
     </xsd:sequence>
     <xsd:attribute name="label" type="xsd:string"/>
+    <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/>
   </xsd:complexType>
 
   <xsd:simpleType name="screen-density">
@@ -158,6 +162,7 @@
       <xsd:element name="locale" type="locale" maxOccurs="unbounded"/>
     </xsd:sequence>
     <xsd:attribute name="label" type="xsd:string"/>
+    <xsd:attribute name="version-code-order" type="xsd:unsignedInt" use="required"/>
   </xsd:complexType>
 
   <xsd:complexType name="locale">
diff --git a/tools/aapt2/configuration/example/config.xml b/tools/aapt2/configuration/example/config.xml
index d8aba09..e6db2a0 100644
--- a/tools/aapt2/configuration/example/config.xml
+++ b/tools/aapt2/configuration/example/config.xml
@@ -36,25 +36,19 @@
   </android-sdks>
 
   <abi-groups>
-    <abi-group label="arm">
+    <abi-group label="arm" version-code-order="1">
       <abi>armeabi-v7a</abi>
       <abi>arm64-v8a</abi>
     </abi-group>
 
-    <abi-group label="other">
+    <abi-group label="other" version-code-order="2">
       <abi>x86</abi>
       <abi>mips</abi>
     </abi-group>
   </abi-groups>
 
   <screen-density-groups>
-    <screen-density-group label="large">
-      <screen-density>xhdpi</screen-density>
-      <screen-density>xxhdpi</screen-density>
-      <screen-density>xxxhdpi</screen-density>
-    </screen-density-group>
-
-    <screen-density-group label="alldpi">
+    <screen-density-group label="alldpi" version-code-order="1">
       <screen-density>ldpi</screen-density>
       <screen-density>mdpi</screen-density>
       <screen-density>hdpi</screen-density>
@@ -62,29 +56,35 @@
       <screen-density>xxhdpi</screen-density>
       <screen-density>xxxhdpi</screen-density>
     </screen-density-group>
+
+    <screen-density-group label="large" version-code-order="2">
+      <screen-density>xhdpi</screen-density>
+      <screen-density>xxhdpi</screen-density>
+      <screen-density>xxxhdpi</screen-density>
+    </screen-density-group>
   </screen-density-groups>
 
   <locale-groups>
-    <locale-group label="europe">
+    <locale-group label="europe" version-code-order="1">
       <locale lang="en"/>
       <locale lang="es"/>
       <locale lang="fr"/>
       <locale lang="de" compressed="true"/>
     </locale-group>
 
-    <locale-group label="north-america">
+    <locale-group label="north-america" version-code-order="2">
       <locale lang="en"/>
       <locale lang="es" region="MX"/>
       <locale lang="fr" region="CA" compressed="true"/>
     </locale-group>
 
-    <locale-group label="all">
+    <locale-group label="all" version-code-order="0">
       <locale compressed="true"/>
     </locale-group>
   </locale-groups>
 
   <gl-texture-groups>
-    <gl-texture-group label="dxt1">
+    <gl-texture-group label="dxt1" version-code-order="1">
       <gl-texture name="GL_EXT_texture_compression_dxt1">
         <texture-path>assets/dxt1/*</texture-path>
       </gl-texture>
@@ -92,7 +92,7 @@
   </gl-texture-groups>
 
   <device-feature-groups>
-    <device-feature-group label="low-latency">
+    <device-feature-group label="low-latency" version-code-order="1">
       <supports-feature>android.hardware.audio.low_latency</supports-feature>
     </device-feature-group>
   </device-feature-groups>
diff --git a/tools/aapt2/link/XmlCompatVersioner_test.cpp b/tools/aapt2/link/XmlCompatVersioner_test.cpp
index 1ed4536..a98ab0f 100644
--- a/tools/aapt2/link/XmlCompatVersioner_test.cpp
+++ b/tools/aapt2/link/XmlCompatVersioner_test.cpp
@@ -23,6 +23,7 @@
 using ::testing::Eq;
 using ::testing::IsNull;
 using ::testing::NotNull;
+using ::testing::Pointee;
 using ::testing::SizeIs;
 
 namespace aapt {
@@ -287,13 +288,13 @@
   ASSERT_THAT(attr, NotNull());
   ASSERT_THAT(attr->compiled_value, NotNull());
   ASSERT_TRUE(attr->compiled_attribute);
-  ASSERT_THAT(*attr->compiled_value, ValueEq(padding_horizontal_value));
+  ASSERT_THAT(attr->compiled_value, Pointee(ValueEq(padding_horizontal_value)));
 
   attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
   ASSERT_THAT(attr, NotNull());
   ASSERT_THAT(attr->compiled_value, NotNull());
   ASSERT_TRUE(attr->compiled_attribute);
-  ASSERT_THAT(*attr->compiled_value, ValueEq(padding_horizontal_value));
+  ASSERT_THAT(attr->compiled_value, Pointee(ValueEq(padding_horizontal_value)));
 
   EXPECT_THAT(versioned_docs[1]->file.config.sdkVersion, Eq(SDK_LOLLIPOP_MR1));
   el = versioned_docs[1]->root.get();
@@ -302,21 +303,20 @@
 
   attr = el->FindAttribute(xml::kSchemaAndroid, "paddingHorizontal");
   ASSERT_THAT(attr, NotNull());
-  ASSERT_THAT(attr->compiled_value, NotNull());
   ASSERT_TRUE(attr->compiled_attribute);
-  ASSERT_THAT(*attr->compiled_value, ValueEq(padding_horizontal_value));
+  ASSERT_THAT(attr->compiled_value, Pointee(ValueEq(padding_horizontal_value)));
 
   attr = el->FindAttribute(xml::kSchemaAndroid, "paddingLeft");
   ASSERT_THAT(attr, NotNull());
   ASSERT_THAT(attr->compiled_value, NotNull());
   ASSERT_TRUE(attr->compiled_attribute);
-  ASSERT_THAT(*attr->compiled_value, ValueEq(padding_horizontal_value));
+  ASSERT_THAT(attr->compiled_value, Pointee(ValueEq(padding_horizontal_value)));
 
   attr = el->FindAttribute(xml::kSchemaAndroid, "paddingRight");
   ASSERT_THAT(attr, NotNull());
   ASSERT_THAT(attr->compiled_value, NotNull());
   ASSERT_TRUE(attr->compiled_attribute);
-  ASSERT_THAT(*attr->compiled_value, ValueEq(padding_horizontal_value));
+  ASSERT_THAT(attr->compiled_value, Pointee(ValueEq(padding_horizontal_value)));
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index 991faad..588b331 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -322,22 +322,56 @@
       std::unique_ptr<xml::Element> new_screens_el = util::make_unique<xml::Element>();
       new_screens_el->name = "compatible-screens";
       screens_el = new_screens_el.get();
-      manifest_el->InsertChild(0, std::move(new_screens_el));
+      manifest_el->AppendChild(std::move(new_screens_el));
     } else {
       // clear out the old element.
       screens_el->GetChildElements().clear();
     }
 
     for (const auto& density : artifact.screen_densities) {
-      std::unique_ptr<xml::Element> screen_el = util::make_unique<xml::Element>();
-      screen_el->name = "screen";
-      const char* density_str = density.toString().string();
-      screen_el->attributes.push_back(xml::Attribute{kSchemaAndroid, "screenDensity", density_str});
-      screens_el->AppendChild(std::move(screen_el));
+      AddScreens(density, screens_el);
     }
   }
 
   return true;
 }
 
+/**
+ * Adds a screen element with both screenSize and screenDensity set. Since we only know the density
+ * we add it for all screen sizes.
+ *
+ * This requires the resource IDs for the attributes from the framework library. Since these IDs are
+ * a part of the public API (and in public.xml) we hard code the values.
+ *
+ * The excert from the framework is as follows:
+ *    <public type="attr" name="screenSize" id="0x010102ca" />
+ *    <public type="attr" name="screenDensity" id="0x010102cb" />
+ */
+void MultiApkGenerator::AddScreens(const ConfigDescription& config, xml::Element* parent) {
+  // Hard coded integer representation of the supported screen sizes:
+  //  small   = 200
+  //  normal  = 300
+  //  large   = 400
+  //  xlarge  = 500
+  constexpr const uint32_t kScreenSizes[4] = {200, 300, 400, 500,};
+  constexpr const uint32_t kScreenSizeResourceId = 0x010102ca;
+  constexpr const uint32_t kScreenDensityResourceId = 0x010102cb;
+
+  for (uint32_t screen_size : kScreenSizes) {
+    std::unique_ptr<xml::Element> screen = util::make_unique<xml::Element>();
+    screen->name = "screen";
+
+    xml::Attribute* size = screen->FindOrCreateAttribute(kSchemaAndroid, "screenSize");
+    size->compiled_attribute = xml::AaptAttribute(Attribute(), {kScreenSizeResourceId});
+    size->compiled_value = ResourceUtils::MakeInt(screen_size);
+
+    xml::Attribute* density = screen->FindOrCreateAttribute(kSchemaAndroid, "screenDensity");
+    density->compiled_attribute = xml::AaptAttribute(Attribute(), {kScreenDensityResourceId});
+    density->compiled_value = ResourceUtils::MakeInt(config.density);
+
+
+    parent->AppendChild(std::move(screen));
+  }
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/optimize/MultiApkGenerator.h b/tools/aapt2/optimize/MultiApkGenerator.h
index 19f64cc..c858879 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.h
+++ b/tools/aapt2/optimize/MultiApkGenerator.h
@@ -63,6 +63,11 @@
   bool UpdateManifest(const configuration::OutputArtifact& artifact,
                       std::unique_ptr<xml::XmlResource>* updated_manifest, IDiagnostics* diag);
 
+  /**
+   * Adds the <screen> elements to the parent node for the provided density configuration.
+   */
+  void AddScreens(const ConfigDescription& config, xml::Element* parent);
+
   LoadedApk* apk_;
   IAaptContext* context_;
 };
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 4e318a9..aca161a 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -146,97 +146,70 @@
   return android::StringPiece16(arg) == a;
 }
 
-class ValueEq {
+template <typename T>
+class ValueEqImpl : public ::testing::MatcherInterface<T> {
  public:
-  template <typename arg_type>
-  class BaseImpl : public ::testing::MatcherInterface<arg_type> {
-    BaseImpl(const BaseImpl&) = default;
-
-    void DescribeTo(::std::ostream* os) const override {
-      *os << "is equal to " << *expected_;
-    }
-
-    void DescribeNegationTo(::std::ostream* os) const override {
-      *os << "is not equal to " << *expected_;
-    }
-
-   protected:
-    BaseImpl(const Value* expected) : expected_(expected) {
-    }
-
-    const Value* expected_;
-  };
-
-  template <typename T, bool>
-  class Impl {};
-
-  template <typename T>
-  class Impl<T, false> : public ::testing::MatcherInterface<T> {
-   public:
-    explicit Impl(const Value* expected) : expected_(expected) {
-    }
-
-    bool MatchAndExplain(T x, ::testing::MatchResultListener* listener) const override {
-      return expected_->Equals(&x);
-    }
-
-    void DescribeTo(::std::ostream* os) const override {
-      *os << "is equal to " << *expected_;
-    }
-
-    void DescribeNegationTo(::std::ostream* os) const override {
-      *os << "is not equal to " << *expected_;
-    }
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(Impl);
-
-    const Value* expected_;
-  };
-
-  template <typename T>
-  class Impl<T, true> : public ::testing::MatcherInterface<T> {
-   public:
-    explicit Impl(const Value* expected) : expected_(expected) {
-    }
-
-    bool MatchAndExplain(T x, ::testing::MatchResultListener* listener) const override {
-      return expected_->Equals(x);
-    }
-
-    void DescribeTo(::std::ostream* os) const override {
-      *os << "is equal to " << *expected_;
-    }
-
-    void DescribeNegationTo(::std::ostream* os) const override {
-      *os << "is not equal to " << *expected_;
-    }
-
-   private:
-    DISALLOW_COPY_AND_ASSIGN(Impl);
-
-    const Value* expected_;
-  };
-
-  ValueEq(const Value& expected) : expected_(&expected) {
+  explicit ValueEqImpl(const Value* expected) : expected_(expected) {
   }
-  ValueEq(const Value* expected) : expected_(expected) {
-  }
-  ValueEq(const ValueEq&) = default;
 
-  template <typename T>
-  operator ::testing::Matcher<T>() const {
-    return ::testing::Matcher<T>(new Impl<T, std::is_pointer<T>::value>(expected_));
+  bool MatchAndExplain(T x, ::testing::MatchResultListener* listener) const override {
+    return expected_->Equals(&x);
+  }
+
+  void DescribeTo(::std::ostream* os) const override {
+    *os << "is equal to " << *expected_;
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const override {
+    *os << "is not equal to " << *expected_;
   }
 
  private:
+  DISALLOW_COPY_AND_ASSIGN(ValueEqImpl);
+
   const Value* expected_;
 };
 
-// MATCHER_P(ValueEq, a,
-//          std::string(negation ? "isn't" : "is") + " equal to " + ::testing::PrintToString(a)) {
-//  return arg.Equals(&a);
-//}
+template <typename TValue>
+class ValueEqMatcher {
+ public:
+  ValueEqMatcher(TValue expected) : expected_(std::move(expected)) {
+  }
+
+  template <typename T>
+  operator ::testing::Matcher<T>() const {
+    return ::testing::Matcher<T>(new ValueEqImpl<T>(&expected_));
+  }
+
+ private:
+  TValue expected_;
+};
+
+template <typename TValue>
+class ValueEqPointerMatcher {
+ public:
+  ValueEqPointerMatcher(const TValue* expected) : expected_(expected) {
+  }
+
+  template <typename T>
+  operator ::testing::Matcher<T>() const {
+    return ::testing::Matcher<T>(new ValueEqImpl<T>(expected_));
+  }
+
+ private:
+  const TValue* expected_;
+};
+
+template <typename TValue,
+          typename = typename std::enable_if<!std::is_pointer<TValue>::value, void>::type>
+inline ValueEqMatcher<TValue> ValueEq(TValue value) {
+  return ValueEqMatcher<TValue>(std::move(value));
+}
+
+template <typename TValue>
+inline ValueEqPointerMatcher<TValue> ValueEq(const TValue* value) {
+  return ValueEqPointerMatcher<TValue>(value);
+}
 
 MATCHER_P(StrValueEq, a,
           std::string(negation ? "isn't" : "is") + " equal to " + ::testing::PrintToString(a)) {
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index fddb6b8..7b748ce 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -244,14 +244,13 @@
       str16 = parser->getAttributeStringValue(i, &len);
       if (str16) {
         attr.value = util::Utf16ToUtf8(StringPiece16(str16, len));
-      } else {
-        android::Res_value res_value;
-        if (parser->getAttributeValue(i, &res_value) > 0) {
-          attr.compiled_value = ResourceUtils::ParseBinaryResValue(
-              ResourceType::kAnim, {}, parser->getStrings(), res_value, out_pool);
-        }
       }
 
+      android::Res_value res_value;
+      if (parser->getAttributeValue(i, &res_value) > 0) {
+        attr.compiled_value = ResourceUtils::ParseBinaryResValue(
+            ResourceType::kAnim, {}, parser->getStrings(), res_value, out_pool);
+      }
 
       el->attributes.push_back(std::move(attr));
     }
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index e5012d6..486b53a 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -23,8 +23,10 @@
 #include "test/Test.h"
 
 using ::aapt::io::StringInputStream;
+using ::aapt::test::ValueEq;
 using ::testing::Eq;
 using ::testing::NotNull;
+using ::testing::Pointee;
 using ::testing::SizeIs;
 using ::testing::StrEq;
 
@@ -59,6 +61,16 @@
   doc->root->name = "Layout";
   doc->root->line_number = 2u;
 
+  xml::Attribute attr;
+  attr.name = "text";
+  attr.namespace_uri = kSchemaAndroid;
+  attr.compiled_attribute = AaptAttribute(
+      aapt::Attribute(android::ResTable_map::TYPE_REFERENCE | android::ResTable_map::TYPE_STRING),
+      ResourceId(0x01010001u));
+  attr.value = "@string/foo";
+  attr.compiled_value = test::BuildReference("string/foo", ResourceId(0x7f010000u));
+  doc->root->attributes.push_back(std::move(attr));
+
   NamespaceDecl decl;
   decl.uri = kSchemaAndroid;
   decl.prefix = "android";
@@ -66,7 +78,9 @@
   doc->root->namespace_decls.push_back(decl);
 
   BigBuffer buffer(4096);
-  XmlFlattener flattener(&buffer, {});
+  XmlFlattenerOptions options;
+  options.keep_raw_values = true;
+  XmlFlattener flattener(&buffer, options);
   ASSERT_TRUE(flattener.Consume(context.get(), doc.get()));
 
   auto block = util::Copy(buffer);
@@ -75,6 +89,21 @@
 
   EXPECT_THAT(new_doc->root->name, StrEq("Layout"));
   EXPECT_THAT(new_doc->root->line_number, Eq(2u));
+
+  ASSERT_THAT(new_doc->root->attributes, SizeIs(1u));
+  EXPECT_THAT(new_doc->root->attributes[0].name, StrEq("text"));
+  EXPECT_THAT(new_doc->root->attributes[0].namespace_uri, StrEq(kSchemaAndroid));
+
+  // We only check that the resource ID was preserved. There is no where to encode the types that
+  // the Attribute accepts (eg: string|reference).
+  ASSERT_TRUE(new_doc->root->attributes[0].compiled_attribute);
+  EXPECT_THAT(new_doc->root->attributes[0].compiled_attribute.value().id,
+              Eq(make_value(ResourceId(0x01010001u))));
+
+  EXPECT_THAT(new_doc->root->attributes[0].value, StrEq("@string/foo"));
+  EXPECT_THAT(new_doc->root->attributes[0].compiled_value,
+              Pointee(ValueEq(Reference(ResourceId(0x7f010000u)))));
+
   ASSERT_THAT(new_doc->root->namespace_decls, SizeIs(1u));
   EXPECT_THAT(new_doc->root->namespace_decls[0].uri, StrEq(kSchemaAndroid));
   EXPECT_THAT(new_doc->root->namespace_decls[0].prefix, StrEq("android"));
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index a78b1a2..f0628c0 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -214,6 +214,7 @@
         fprintf(out, "{\n");
         argIndex = 1;
         fprintf(out, "    android_log_event_list event(kStatsEventTag);\n");
+        fprintf(out, "    event << android::elapsedRealtimeNano();\n\n");
         fprintf(out, "    event << code;\n\n");
         for (vector<java_type_t>::const_iterator arg = signature->begin();
             arg != signature->end(); arg++) {
diff --git a/wifi/OWNERS b/wifi/OWNERS
new file mode 100644
index 0000000..0efa464
--- /dev/null
+++ b/wifi/OWNERS
@@ -0,0 +1,5 @@
+set noparent
+
+etancohen@google.com
+satk@google.com
+silberst@google.com
diff --git a/wifi/java/android/net/wifi/rtt/RangingRequest.java b/wifi/java/android/net/wifi/rtt/RangingRequest.java
index 52b3d86..4348ed4 100644
--- a/wifi/java/android/net/wifi/rtt/RangingRequest.java
+++ b/wifi/java/android/net/wifi/rtt/RangingRequest.java
@@ -122,6 +122,11 @@
          * Add the device specified by the {@link ScanResult} to the list of devices with
          * which to measure range. The total number of peers added to a request cannot exceed the
          * limit specified by {@link #getMaxPeers()}.
+         * <p>
+         * Ranging may not be supported if the Access Point does not support IEEE 802.11mc. Use
+         * {@link ScanResult#is80211mcResponder()} to verify the Access Point's capabilities. If
+         * not supported the result status will be
+         * {@link RangingResult#STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC}.
          *
          * @param apInfo Information of an Access Point (AP) obtained in a Scan Result.
          * @return The builder to facilitate chaining
@@ -138,6 +143,11 @@
          * Add the devices specified by the {@link ScanResult}s to the list of devices with
          * which to measure range. The total number of peers added to a request cannot exceed the
          * limit specified by {@link #getMaxPeers()}.
+         * <p>
+         * Ranging may not be supported if the Access Point does not support IEEE 802.11mc. Use
+         * {@link ScanResult#is80211mcResponder()} to verify the Access Point's capabilities. If
+         * not supported the result status will be
+         * {@link RangingResult#STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC}.
          *
          * @param apInfos Information of an Access Points (APs) obtained in a Scan Result.
          * @return The builder to facilitate chaining
diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.java b/wifi/java/android/net/wifi/rtt/RangingResult.java
index f7c8567..5c9bd19 100644
--- a/wifi/java/android/net/wifi/rtt/RangingResult.java
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.java
@@ -42,7 +42,7 @@
     private static final String TAG = "RangingResult";
 
     /** @hide */
-    @IntDef({STATUS_SUCCESS, STATUS_FAIL})
+    @IntDef({STATUS_SUCCESS, STATUS_FAIL, STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC})
     @Retention(RetentionPolicy.SOURCE)
     public @interface RangeResultStatus {
     }
@@ -67,8 +67,6 @@
      * <p>
      * On such a failure, the individual result fields of {@link RangingResult} such as
      * {@link RangingResult#getDistanceMm()} are invalid.
-     *
-     * @hide
      */
     public static final int STATUS_RESPONDER_DOES_NOT_SUPPORT_IEEE80211MC = 2;