Merge "Statsd guardrail for Alerts"
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 65c7b0e3..df18f10 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12555,6 +12555,7 @@
     method public abstract void onUpgrade(android.database.sqlite.SQLiteDatabase, int, int);
     method public void setIdleConnectionTimeout(long);
     method public void setLookasideConfig(int, int);
+    method public void setOpenParams(android.database.sqlite.SQLiteDatabase.OpenParams);
     method public void setWriteAheadLoggingEnabled(boolean);
   }
 
@@ -13838,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();
@@ -26756,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";
@@ -27032,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
@@ -27040,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
@@ -28559,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
   }
 
@@ -38684,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;
@@ -38702,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();
   }
 
@@ -38867,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();
@@ -43228,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 {
   }
 
@@ -43239,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);
@@ -43395,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);
@@ -53647,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();
@@ -53753,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);
@@ -53779,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..44349dc 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;
   }
 
@@ -4172,6 +4175,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/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 4ee9471..8663e5e 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -138,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/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/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/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index a2991e6..64e9e5d 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -58,7 +58,7 @@
 
     private SQLiteDatabase mDatabase;
     private boolean mIsInitializing;
-    private final SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder;
+    private SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder;
 
     /**
      * Create a helper object to create, open, and/or manage a database.
@@ -163,8 +163,7 @@
         mName = name;
         mNewVersion = version;
         mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion);
-        mOpenParamsBuilder = openParamsBuilder;
-        mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY);
+        setOpenParamsBuilder(openParamsBuilder);
     }
 
     /**
@@ -230,6 +229,30 @@
     }
 
     /**
+     * Sets configuration parameters that are used for opening {@link SQLiteDatabase}.
+     * <p>Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be set when
+     * opening the database
+     *
+     * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}.
+     * @throws IllegalStateException if the database is already open
+     */
+    public void setOpenParams(@NonNull SQLiteDatabase.OpenParams openParams) {
+        Preconditions.checkNotNull(openParams);
+        synchronized (this) {
+            if (mDatabase != null && mDatabase.isOpen()) {
+                throw new IllegalStateException(
+                        "OpenParams cannot be set after opening the database");
+            }
+            setOpenParamsBuilder(new SQLiteDatabase.OpenParams.Builder(openParams));
+        }
+    }
+
+    private void setOpenParamsBuilder(SQLiteDatabase.OpenParams.Builder openParamsBuilder) {
+        mOpenParamsBuilder = openParamsBuilder;
+        mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY);
+    }
+
+    /**
      * Sets the maximum number of milliseconds that SQLite connection is allowed to be idle
      * before it is closed and removed from the pool.
      *
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/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..630b63f 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
          */
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/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 370c97e..e50d40e 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -331,6 +331,7 @@
 
     private static final int FLAG_DUMP_FRAMESTATS   = 1 << 0;
     private static final int FLAG_DUMP_RESET        = 1 << 1;
+    private static final int FLAG_DUMP_ALL          = FLAG_DUMP_FRAMESTATS;
 
     @IntDef(flag = true, prefix = { "FLAG_DUMP_" }, value = {
             FLAG_DUMP_FRAMESTATS,
@@ -636,7 +637,10 @@
      */
     void dumpGfxInfo(PrintWriter pw, FileDescriptor fd, String[] args) {
         pw.flush();
-        int flags = 0;
+        // If there's no arguments, eg 'dumpsys gfxinfo', then dump everything.
+        // If there's a targetted package, eg 'dumpsys gfxinfo com.android.systemui', then only
+        // dump the summary information
+        int flags = (args == null || args.length == 0) ? FLAG_DUMP_ALL : 0;
         for (int i = 0; i < args.length; i++) {
             switch (args[i]) {
                 case "framestats":
@@ -645,6 +649,9 @@
                 case "reset":
                     flags |= FLAG_DUMP_RESET;
                     break;
+                case "-a": // magic option passed when dumping a bugreport.
+                    flags = FLAG_DUMP_ALL;
+                    break;
             }
         }
         nDumpProfileInfo(mNativeProxy, fd, flags);
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/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/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/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/values/config.xml b/core/res/res/values/config.xml
index 0218750..1f4425f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3316,4 +3316,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..59c742e 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -547,7 +547,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
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5a9dc7f..b69ded8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3262,4 +3262,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..b367f29 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -468,6 +468,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 +496,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/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.mk b/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.mk
index 994131a..9b9e811 100644
--- a/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.mk
+++ b/core/tests/hosttests/test-apps/SharedUid/32/jni/Android.mk
@@ -29,13 +29,10 @@
 LOCAL_SRC_FILES:= \
   native.cpp
 
-# All of the shard libraries we link against.
-LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_LDLIBS := -llog
 
 LOCAL_CFLAGS += -Wall -Wextra -Werror
 
-# Also need the JNI headers.
-LOCAL_C_INCLUDES += \
-	$(JNI_H_INCLUDE)
+LOCAL_SDK_VERSION := current
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp b/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp
index 99cf587..fe32454 100644
--- a/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp
+++ b/core/tests/hosttests/test-apps/SharedUid/32/jni/native.cpp
@@ -15,12 +15,15 @@
  */
 
 #define LOG_TAG "pmtest32 native.cpp"
-#include <utils/Log.h>
+#include <android/log.h>
 
 #include <stdio.h>
 
 #include "jni.h"
 
+#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
 static jint
 add(JNIEnv */* env */, jobject /* thiz */, jint a, jint b) {
 int result = a + b;
diff --git a/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.mk b/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.mk
index 6c2679b..600a5d1 100644
--- a/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.mk
+++ b/core/tests/hosttests/test-apps/SharedUid/64/jni/Android.mk
@@ -30,14 +30,10 @@
 LOCAL_SRC_FILES:= \
   native.cpp
 
-# All of the shared libraries we link against.
-LOCAL_SHARED_LIBRARIES := \
-	libutils liblog
+LOCAL_LDLIBS := -llog
 
 LOCAL_CFLAGS += -Wall -Wextra -Werror
 
-# Also need the JNI headers.
-LOCAL_C_INCLUDES += \
-	$(JNI_H_INCLUDE)
+LOCAL_SDK_VERSION := current
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp b/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp
index 0b6d750..ad9e746 100644
--- a/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp
+++ b/core/tests/hosttests/test-apps/SharedUid/64/jni/native.cpp
@@ -15,12 +15,15 @@
  */
 
 #define LOG_TAG "pmtest64 native.cpp"
-#include <utils/Log.h>
+#include <android/log.h>
 
 #include <stdio.h>
 
 #include "jni.h"
 
+#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
 static jint
 add(JNIEnv */* env */, jobject /* thiz */, jint a, jint b) {
 int result = a + b;
diff --git a/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.mk b/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.mk
index d668f29..8e9ac6b 100644
--- a/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.mk
+++ b/core/tests/hosttests/test-apps/SharedUid/dual/jni/Android.mk
@@ -29,14 +29,10 @@
 LOCAL_SRC_FILES:= \
   native.cpp
 
-# All of the shard libraries we link against.
 LOCAL_LDLIBS = -llog
-LOCAL_SHARED_LIBRARIES := liblog
 
 LOCAL_CFLAGS += -Wall -Wextra -Werror
 
-# Also need the JNI headers.
-LOCAL_C_INCLUDES += \
-	$(JNI_H_INCLUDE)
+LOCAL_SDK_VERSION := current
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp b/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp
index 3947e21..5c5088f 100644
--- a/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp
+++ b/core/tests/hosttests/test-apps/SharedUid/dual/jni/native.cpp
@@ -15,12 +15,15 @@
  */
 
 #define LOG_TAG "pmtestdual native.cpp"
-#include <utils/Log.h>
+#include <android/log.h>
 
 #include <stdio.h>
 
 #include "jni.h"
 
+#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+
 static jint
 add(JNIEnv */* env */, jobject /* thiz */, jint a, jint b) {
 int result = a + b;
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/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/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/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index af6dd77..7c97ca61 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -2,19 +2,23 @@
 
 dsandler@google.com
 
+adamcohen@google.com
 asc@google.com
 ashaikh@google.com
 beverlyt@google.com
 cinek@google.com
 cwren@google.com
+dupin@google.com
 evanlaird@google.com
 jmonk@google.com
 jaggies@google.com
 jjaggi@google.com
 juliacr@google.com
-dupin@google.com
 madym@google.com
+ngmatthew@google.com
 roosa@google.com
 shahrk@google.com
+sunnygoyal@google.com
+twickham@google.com
 winsonc@google.com
 
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 97472a4..cf88ade 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -71,23 +71,19 @@
                 android:singleLine="true" />
         </LinearLayout>
 
-        <FrameLayout
+        <View
+            android:id="@+id/qs_drag_handle_view"
             android:layout_width="24dp"
-            android:layout_height="match_parent" >
-            <View
-                android:id="@+id/qs_drag_handle_view"
-                android:layout_width="match_parent"
-                android:layout_height="4dp"
-                android:layout_marginTop="28dp"
-                android:background="@drawable/qs_footer_drag_handle" />
-        </FrameLayout>
+            android:layout_height="4dp"
+            android:layout_gravity="center"
+            android:background="@drawable/qs_footer_drag_handle" />
 
-        <LinearLayout
+        <com.android.keyguard.AlphaOptimizedLinearLayout
             android:id="@+id/qs_footer_actions_container"
             android:layout_width="0dp"
             android:layout_height="match_parent"
             android:layout_weight="1"
-            android:gravity="end" >
+            android:gravity="center_vertical|end" >
             <com.android.systemui.statusbar.phone.MultiUserSwitch
                 android:id="@+id/multi_user_switch"
                 android:layout_width="48dp"
@@ -113,7 +109,7 @@
                 android:clipToPadding="false"
                 android:contentDescription="@string/accessibility_quick_settings_edit"
                 android:focusable="true"
-                android:padding="16dp"
+                android:padding="15dp"
                 android:src="@drawable/ic_mode_edit"
                 android:tint="?android:attr/colorForeground"/>
 
@@ -131,6 +127,7 @@
                     android:layout_height="match_parent"
                     android:background="@drawable/ripple_drawable"
                     android:contentDescription="@string/accessibility_quick_settings_settings"
+                    android:padding="15dp"
                     android:src="@drawable/ic_settings_16dp"
                     android:tint="?android:attr/colorForeground"/>
 
@@ -145,7 +142,7 @@
                     android:visibility="invisible"/>
 
             </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
-        </LinearLayout>
+        </com.android.keyguard.AlphaOptimizedLinearLayout>
     </LinearLayout>
 
 </com.android.systemui.qs.QSFooterImpl>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a62f38b..d11ab42 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -230,7 +230,7 @@
 
     <!-- The height of the quick settings footer that holds the user switcher, settings icon,
          etc. -->
-    <dimen name="qs_footer_height">48dp</dimen>
+    <dimen name="qs_footer_height">56dp</dimen>
 
     <!-- The padding between the notifications and the quick settings container -->
     <dimen name="qs_notification_padding">@dimen/notification_side_paddings</dimen>
@@ -337,7 +337,7 @@
     <dimen name="qs_footer_padding_end">24dp</dimen>
     <dimen name="qs_footer_icon_size">16dp</dimen>
     <!-- Difference between drag handle margin in QQS and expanded QS -->
-    <dimen name="qs_footer_drag_handle_offset">6dp</dimen>
+    <dimen name="qs_footer_drag_handle_offset">10dp</dimen>
 
     <dimen name="qs_notif_collapsed_space">64dp</dimen>
 
@@ -513,7 +513,7 @@
     <dimen name="multi_user_avatar_keyguard_size">22dp</dimen>
 
     <!-- The width of user avatar when expanded -->
-    <dimen name="multi_user_avatar_expanded_size">16dp</dimen>
+    <dimen name="multi_user_avatar_expanded_size">18dp</dimen>
 
     <!-- The font size of the time when collapsed in QS -->
     <dimen name="qs_time_collapsed_size">14sp</dimen>
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/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index fe3ffb9..b9919a3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -174,7 +174,8 @@
                 .addFloat(mDivider, "alpha", 0, 1)
                 .addFloat(mCarrierText, "alpha", 0, 1)
                 .addFloat(mActionsContainer, "alpha", 0, 1)
-                .addFloat(mDragHandle, "translationY", 0, -mDragHandleExpandOffset)
+                .addFloat(mDragHandle, "translationY", mDragHandleExpandOffset, 0)
+                .addFloat(mDragHandle, "alpha", 1, 0)
                 .build();
     }
 
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/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
index bb2f597..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;
@@ -89,6 +86,7 @@
     private int mLightTrackColor;
     private int mDarkTrackColor;
     private float mDarkIntensity;
+    private View mHomeButtonView;
 
     private final Handler mHandler = new Handler();
     private final Interpolator mQuickScrubEndInterpolator = new DecelerateInterpolator();
@@ -114,11 +112,10 @@
         if (!mQuickScrubActive) {
             pos = mDragPositive ? Math.min((int) mTranslation, pos) : Math.max((int) mTranslation, pos);
         }
-        final View homeView = mNavigationBarView.getHomeButton().getCurrentView();
         if (mIsVertical) {
-            homeView.setTranslationY(pos);
+            mHomeButtonView.setTranslationY(pos);
         } else {
-            homeView.setTranslationX(pos);
+            mHomeButtonView.setTranslationX(pos);
         }
     };
 
@@ -126,6 +123,7 @@
         @Override
         public void onAnimationEnd(Animator animation) {
             mNavigationBarView.getHomeButton().setClickable(true);
+            mHomeButtonView = null;
             mQuickScrubActive = false;
             mTranslation = 0;
         }
@@ -137,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;
@@ -195,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;
         }
@@ -226,7 +224,8 @@
             case MotionEvent.ACTION_DOWN: {
                 int x = (int) event.getX();
                 int y = (int) event.getY();
-                if (isQuickScrubEnabled()
+                mHomeButtonView = homeButton.getCurrentView();
+                if (mNavigationBarView.isQuickScrubEnabled()
                         && mNavigationBarView.getDownHitTarget() == HIT_TARGET_HOME) {
                     mTouchDownX = x;
                     mTouchDownY = y;
@@ -294,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);
                                 }
@@ -305,9 +304,9 @@
                             mTranslation /= SWITCH_STICKINESS;
                         }
                         if (mIsVertical) {
-                            homeButton.getCurrentView().setTranslationY(mTranslation);
+                            mHomeButtonView.setTranslationY(mTranslation);
                         } else {
-                            homeButton.getCurrentView().setTranslationX(mTranslation);
+                            mHomeButtonView.setTranslationX(mTranslation);
                         }
                     }
                 }
@@ -315,7 +314,7 @@
             }
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
-                endQuickScrub();
+                endQuickScrub(true /* animate */);
                 break;
         }
         return mDraggingActive || mQuickScrubActive;
@@ -357,6 +356,11 @@
 
     @Override
     public void setBarState(boolean isVertical, boolean isRTL) {
+        final boolean changed = (mIsVertical != isVertical) || (mIsRTL != isRTL);
+        if (changed) {
+            // End quickscrub if the state changes mid-transition
+            endQuickScrub(false /* animate */);
+        }
         mIsVertical = isVertical;
         mIsRTL = isRTL;
         try {
@@ -370,10 +374,6 @@
         }
     }
 
-    boolean isQuickScrubEnabled() {
-        return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true);
-    }
-
     private void startQuickScrub() {
         if (!mQuickScrubActive) {
             mQuickScrubActive = true;
@@ -392,7 +392,7 @@
         }
     }
 
-    private void endQuickScrub() {
+    private void endQuickScrub(boolean animate) {
         mHandler.removeCallbacks(mLongPressRunnable);
         if (mDraggingActive || mQuickScrubActive) {
             mButtonAnimator.setIntValues((int) mTranslation, 0);
@@ -406,6 +406,9 @@
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to send end of quick scrub.", e);
             }
+            if (!animate) {
+                mQuickScrubEndAnimator.end();
+            }
         }
         mDraggingActive = false;
     }
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/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/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index c36bb6d..5b5d18f 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -446,8 +446,10 @@
             mMagnificationRegion.getBounds(viewport);
             final MagnificationSpec spec = mCurrentMagnificationSpec;
             final float oldScale = spec.scale;
-            final float oldCenterX = (viewport.width() / 2.0f - spec.offsetX) / oldScale;
-            final float oldCenterY = (viewport.height() / 2.0f - spec.offsetY) / oldScale;
+            final float oldCenterX
+                    = (viewport.width() / 2.0f - spec.offsetX + viewport.left) / oldScale;
+            final float oldCenterY
+                    = (viewport.height() / 2.0f - spec.offsetY + viewport.top) / oldScale;
             final float normPivotX = (pivotX - spec.offsetX) / oldScale;
             final float normPivotY = (pivotY - spec.offsetY) / oldScale;
             final float offsetX = (oldCenterX - normPivotX) * (oldScale / scale);
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..0dcefbf 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
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 fffe7dc..76e0d89 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -551,8 +551,6 @@
     // Used to play ringtones outside system_server
     private volatile IRingtonePlayer mRingtonePlayer;
 
-    private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
-
     // Request to override default use of A2DP for media.
     private boolean mBluetoothA2dpEnabled;
     private final Object mBluetoothA2dpEnabledLock = new Object();
@@ -571,8 +569,6 @@
             AudioSystem.DEVICE_OUT_AUX_LINE;
     int mFullVolumeDevices = 0;
 
-    // TODO merge orientation and rotation
-    private final boolean mMonitorOrientation;
     private final boolean mMonitorRotation;
 
     private boolean mDockAudioMediaEnabled = true;
@@ -788,13 +784,6 @@
         intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
 
         intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-        // TODO merge orientation and rotation
-        mMonitorOrientation = SystemProperties.getBoolean("ro.audio.monitorOrientation", false);
-        if (mMonitorOrientation) {
-            Log.v(TAG, "monitoring device orientation");
-            // initialize orientation in AudioSystem
-            setOrientationForAudioSystem();
-        }
         mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false);
         if (mMonitorRotation) {
             RotationHelper.init(mContext, mAudioHandler);
@@ -963,10 +952,7 @@
         // Restore ringer mode
         setRingerModeInt(getRingerModeInternal(), false);
 
-        // Reset device orientation (if monitored for this device)
-        if (mMonitorOrientation) {
-            setOrientationForAudioSystem();
-        }
+        // Reset device rotation (if monitored for this device)
         if (mMonitorRotation) {
             RotationHelper.updateOrientation();
         }
@@ -1042,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();
+                    }
                 }
             }
         }
@@ -1155,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();
@@ -4604,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);
             }
         }
 
@@ -6192,24 +6180,15 @@
     // Device orientation
     //==========================================================================================
     /**
-     * Handles device configuration changes that may map to a change in the orientation
-     * or orientation.
-     * Monitoring orientation and rotation is optional, and is defined by the definition and value
-     * of the "ro.audio.monitorOrientation" and "ro.audio.monitorRotation" system properties.
+     * Handles device configuration changes that may map to a change in rotation.
+     * Monitoring rotation is optional, and is defined by the definition and value
+     * of the "ro.audio.monitorRotation" system property.
      */
     private void handleConfigurationChanged(Context context) {
         try {
-            // reading new orientation "safely" (i.e. under try catch) in case anything
-            // goes wrong when obtaining resources and configuration
+            // reading new configuration "safely" (i.e. under try catch) in case anything
+            // goes wrong.
             Configuration config = context.getResources().getConfiguration();
-            // TODO merge rotation and orientation
-            if (mMonitorOrientation) {
-                int newOrientation = config.orientation;
-                if (newOrientation != mDeviceOrientation) {
-                    mDeviceOrientation = newOrientation;
-                    setOrientationForAudioSystem();
-                }
-            }
             sendMsg(mAudioHandler,
                     MSG_CONFIGURE_SAFE_MEDIA_VOLUME,
                     SENDMSG_REPLACE,
@@ -6224,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);
@@ -6261,30 +6242,6 @@
         }
     }
 
-    //TODO move to an external "orientation helper" class
-    private void setOrientationForAudioSystem() {
-        switch (mDeviceOrientation) {
-            case Configuration.ORIENTATION_LANDSCAPE:
-                //Log.i(TAG, "orientation is landscape");
-                AudioSystem.setParameters("orientation=landscape");
-                break;
-            case Configuration.ORIENTATION_PORTRAIT:
-                //Log.i(TAG, "orientation is portrait");
-                AudioSystem.setParameters("orientation=portrait");
-                break;
-            case Configuration.ORIENTATION_SQUARE:
-                //Log.i(TAG, "orientation is square");
-                AudioSystem.setParameters("orientation=square");
-                break;
-            case Configuration.ORIENTATION_UNDEFINED:
-                //Log.i(TAG, "orientation is undefined");
-                AudioSystem.setParameters("orientation=undefined");
-                break;
-            default:
-                Log.e(TAG, "Unknown orientation");
-        }
-    }
-
     // Handles request to override default use of A2DP for media.
     // Must be called synchronized on mConnectedDevices
     public void setBluetoothA2dpOnInt(boolean on, String eventSource) {
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/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/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/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/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/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 78b6077..cbbdca6 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -308,6 +308,7 @@
     private void reportEvent(AppStandbyController controller, int eventType,
             long elapsedTime) {
         // Back to ACTIVE on event
+        mInjector.mElapsedRealtime = elapsedTime;
         UsageEvents.Event ev = new UsageEvents.Event();
         ev.mPackage = PACKAGE_1;
         ev.mEventType = eventType;
@@ -487,6 +488,89 @@
     }
 
     @Test
+    public void testCascadingTimeouts() throws Exception {
+        setChargingState(mController, false);
+
+        reportEvent(mController, USER_INTERACTION, 0);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        reportEvent(mController, NOTIFICATION_SEEN, 1000);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_PREDICTED, 1000);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+                REASON_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis);
+        assertBucket(STANDBY_BUCKET_WORKING_SET);
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+                REASON_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis);
+        assertBucket(STANDBY_BUCKET_FREQUENT);
+    }
+
+    @Test
+    public void testOverlappingTimeouts() throws Exception {
+        setChargingState(mController, false);
+
+        reportEvent(mController, USER_INTERACTION, 0);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        reportEvent(mController, NOTIFICATION_SEEN, 1000);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // Overlapping USER_INTERACTION before previous one times out
+        reportEvent(mController, USER_INTERACTION, mController.mStrongUsageTimeoutMillis - 1000);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // Still in ACTIVE after first USER_INTERACTION times out
+        mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis + 1000;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+                REASON_PREDICTED, mInjector.mElapsedRealtime);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // Both timed out, so NOTIFICATION_SEEN timeout should be effective
+        mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis * 2 + 2000;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+                REASON_PREDICTED, mInjector.mElapsedRealtime);
+        assertBucket(STANDBY_BUCKET_WORKING_SET);
+
+        mInjector.mElapsedRealtime = mController.mNotificationSeenTimeoutMillis + 2000;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+                REASON_PREDICTED, mInjector.mElapsedRealtime);
+        assertBucket(STANDBY_BUCKET_RARE);
+    }
+
+    @Test
+    public void testPredictionNotOverridden() throws Exception {
+        setChargingState(mController, false);
+
+        reportEvent(mController, USER_INTERACTION, 0);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        mInjector.mElapsedRealtime = WORKING_SET_THRESHOLD - 1000;
+        reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // Falls back to WORKING_SET
+        mInjector.mElapsedRealtime += 5000;
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_WORKING_SET);
+
+        // Predict to ACTIVE
+        mInjector.mElapsedRealtime += 1000;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+                REASON_PREDICTED, mInjector.mElapsedRealtime);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // CheckIdleStates should not change the prediction
+        mInjector.mElapsedRealtime += 1000;
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+    }
+
+    @Test
     public void testAddActiveDeviceAdmin() {
         assertActiveAdmins(USER_ID, (String[]) null);
         assertActiveAdmins(USER_ID2, (String[]) null);
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index b654a66..f26c2ae 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -23,6 +23,7 @@
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
 
 import android.app.usage.UsageStatsManager;
 import android.os.SystemClock;
@@ -87,7 +88,9 @@
     // The last time a job was run for this app
     private static final String ATTR_LAST_RUN_JOB_TIME = "lastJobRunTime";
     // The time when the forced active state can be overridden.
-    private static final String ATTR_BUCKET_TIMEOUT_TIME = "bucketTimeoutTime";
+    private static final String ATTR_BUCKET_ACTIVE_TIMEOUT_TIME = "activeTimeoutTime";
+    // The time when the forced working_set state can be overridden.
+    private static final String ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME = "workingSetTimeoutTime";
 
     // device on time = mElapsedDuration + (timeNow - mElapsedSnapshot)
     private long mElapsedSnapshot; // Elapsed time snapshot when last write of mDeviceOnDuration
@@ -117,11 +120,15 @@
         int lastInformedBucket;
         // The last time a job was run for this app, using elapsed timebase
         long lastJobRunTime;
-        // When should the bucket state timeout, in elapsed timebase, if greater than
+        // When should the bucket active state timeout, in elapsed timebase, if greater than
         // lastUsedElapsedTime.
         // This is used to keep the app in a high bucket regardless of other timeouts and
         // predictions.
-        long bucketTimeoutTime;
+        long bucketActiveTimeoutTime;
+        // If there's a forced working_set state, this is when it times out. This can be sitting
+        // under any active state timeout, so that it becomes applicable after the active state
+        // timeout expires.
+        long bucketWorkingSetTimeoutTime;
     }
 
     AppIdleHistory(File storageDir, long elapsedRealtime) {
@@ -208,11 +215,28 @@
      * @param packageName name of the app being updated, for logging purposes
      * @param newBucket the bucket to set the app to
      * @param elapsedRealtime mark as used time if non-zero
-     * @param timeout set the timeout of the specified bucket, if non-zero
+     * @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
+     *                with bucket values of ACTIVE and WORKING_SET.
      * @return
      */
     public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName,
             int newBucket, long elapsedRealtime, long timeout) {
+        // Set the timeout if applicable
+        if (timeout > elapsedRealtime) {
+            // Convert to elapsed timebase
+            final long timeoutTime = mElapsedDuration + (timeout - mElapsedSnapshot);
+            if (newBucket == STANDBY_BUCKET_ACTIVE) {
+                appUsageHistory.bucketActiveTimeoutTime = Math.max(timeoutTime,
+                        appUsageHistory.bucketActiveTimeoutTime);
+            } else if (newBucket == STANDBY_BUCKET_WORKING_SET) {
+                appUsageHistory.bucketWorkingSetTimeoutTime = Math.max(timeoutTime,
+                        appUsageHistory.bucketWorkingSetTimeoutTime);
+            } else {
+                throw new IllegalArgumentException("Cannot set a timeout on bucket=" +
+                        newBucket);
+            }
+        }
+
         if (elapsedRealtime != 0) {
             appUsageHistory.lastUsedElapsedTime = mElapsedDuration
                     + (elapsedRealtime - mElapsedSnapshot);
@@ -226,12 +250,6 @@
                         .currentBucket
                         + ", reason=" + appUsageHistory.bucketingReason);
             }
-            if (timeout > elapsedRealtime) {
-                // Convert to elapsed timebase
-                appUsageHistory.bucketTimeoutTime =
-                        Math.max(appUsageHistory.bucketTimeoutTime,
-                                mElapsedDuration + (timeout - mElapsedSnapshot));
-            }
         }
         appUsageHistory.bucketingReason = REASON_USAGE;
 
@@ -247,7 +265,8 @@
      * @param userId
      * @param newBucket the bucket to set the app to
      * @param elapsedRealtime mark as used time if non-zero
-     * @param timeout set the timeout of the specified bucket, if non-zero
+     * @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
+     *                with bucket values of ACTIVE and WORKING_SET.
      * @return
      */
     public AppUsageHistory reportUsage(String packageName, int userId, int newBucket,
@@ -504,8 +523,10 @@
                                 parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
                         appUsageHistory.lastJobRunTime = getLongValue(parser,
                                 ATTR_LAST_RUN_JOB_TIME, Long.MIN_VALUE);
-                        appUsageHistory.bucketTimeoutTime = getLongValue(parser,
-                                ATTR_BUCKET_TIMEOUT_TIME, 0L);
+                        appUsageHistory.bucketActiveTimeoutTime = getLongValue(parser,
+                                ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, 0L);
+                        appUsageHistory.bucketWorkingSetTimeoutTime = getLongValue(parser,
+                                ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, 0L);
                         if (appUsageHistory.bucketingReason == null) {
                             appUsageHistory.bucketingReason = REASON_DEFAULT;
                         }
@@ -557,9 +578,13 @@
                 xml.attribute(null, ATTR_CURRENT_BUCKET,
                         Integer.toString(history.currentBucket));
                 xml.attribute(null, ATTR_BUCKETING_REASON, history.bucketingReason);
-                if (history.bucketTimeoutTime > 0) {
-                    xml.attribute(null, ATTR_BUCKET_TIMEOUT_TIME, Long.toString(history
-                            .bucketTimeoutTime));
+                if (history.bucketActiveTimeoutTime > 0) {
+                    xml.attribute(null, ATTR_BUCKET_ACTIVE_TIMEOUT_TIME, Long.toString(history
+                            .bucketActiveTimeoutTime));
+                }
+                if (history.bucketWorkingSetTimeoutTime > 0) {
+                    xml.attribute(null, ATTR_BUCKET_WORKING_SET_TIMEOUT_TIME, Long.toString(history
+                            .bucketWorkingSetTimeoutTime));
                 }
                 if (history.lastJobRunTime != Long.MIN_VALUE) {
                     xml.attribute(null, ATTR_LAST_RUN_JOB_TIME, Long.toString(history
@@ -593,14 +618,19 @@
                 continue;
             }
             idpw.print("package=" + packageName);
+            idpw.print(" userId=" + userId);
             idpw.print(" lastUsedElapsed=");
             TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastUsedElapsedTime, idpw);
             idpw.print(" lastUsedScreenOn=");
             TimeUtils.formatDuration(screenOnTime - appUsageHistory.lastUsedScreenTime, idpw);
             idpw.print(" lastPredictedTime=");
             TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastPredictedTime, idpw);
-            idpw.print(" bucketTimeoutTime=");
-            TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketTimeoutTime, idpw);
+            idpw.print(" bucketActiveTimeoutTime=");
+            TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketActiveTimeoutTime,
+                    idpw);
+            idpw.print(" bucketWorkingSetTimeoutTime=");
+            TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.bucketWorkingSetTimeoutTime,
+                    idpw);
             idpw.print(" lastJobRunTime=");
             TimeUtils.formatDuration(totalElapsedTime - appUsageHistory.lastJobRunTime, idpw);
             idpw.print(" idle=" + (isIdle(packageName, userId, elapsedRealtime) ? "y" : "n"));
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 32db752..c31809e 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -30,6 +30,7 @@
 import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
 import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
 
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.usage.UsageStatsManager.StandbyBuckets;
@@ -171,6 +172,8 @@
     static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
     static final int MSG_PAROLE_STATE_CHANGED = 9;
     static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
+    /** Check the state of one app: arg1 = userId, arg2 = uid, obj = (String) packageName */
+    static final int MSG_CHECK_PACKAGE_IDLE_STATE = 11;
 
     long mCheckIdleIntervalMillis;
     long mAppIdleParoleIntervalMillis;
@@ -322,7 +325,7 @@
         // Get sync adapters for the authority
         String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
                 authority, userId);
-        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long elapsedRealtime = mInjector.elapsedRealtime();
         for (String packageName: packages) {
             // Only force the sync adapters to active if the provider is not in the same package and
             // the sync adapter is a system package.
@@ -460,53 +463,8 @@
             for (int p = 0; p < packageCount; p++) {
                 final PackageInfo pi = packages.get(p);
                 final String packageName = pi.packageName;
-                final boolean isSpecial = isAppSpecial(packageName,
-                        UserHandle.getAppId(pi.applicationInfo.uid),
-                        userId);
-                if (DEBUG) {
-                    Slog.d(TAG, "   Checking idle state for " + packageName + " special=" +
-                            isSpecial);
-                }
-                if (isSpecial) {
-                    synchronized (mAppIdleLock) {
-                        mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
-                                STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT);
-                    }
-                    maybeInformListeners(packageName, userId, elapsedRealtime,
-                            STANDBY_BUCKET_EXEMPTED, false);
-                } else {
-                    synchronized (mAppIdleLock) {
-                        AppIdleHistory.AppUsageHistory app =
-                                mAppIdleHistory.getAppUsageHistory(packageName,
-                                userId, elapsedRealtime);
-                        // If the bucket was forced by the developer or the app is within the
-                        // temporary active period, leave it alone.
-                        if (REASON_FORCED.equals(app.bucketingReason)
-                                || !hasBucketTimeoutPassed(app, elapsedRealtime)) {
-                            continue;
-                        }
-                        boolean predictionLate = false;
-                        // If the bucket was moved up due to usage, let the timeouts apply.
-                        if (REASON_DEFAULT.equals(app.bucketingReason)
-                                || REASON_USAGE.equals(app.bucketingReason)
-                                || REASON_TIMEOUT.equals(app.bucketingReason)
-                                || (predictionLate = predictionTimedOut(app, elapsedRealtime))) {
-                            int oldBucket = app.currentBucket;
-                            int newBucket = getBucketForLocked(packageName, userId,
-                                    elapsedRealtime);
-                            if (DEBUG) {
-                                Slog.d(TAG, "     Old bucket=" + oldBucket
-                                        + ", newBucket=" + newBucket);
-                            }
-                            if (oldBucket < newBucket || predictionLate) {
-                                mAppIdleHistory.setAppStandbyBucket(packageName, userId,
-                                        elapsedRealtime, newBucket, REASON_TIMEOUT);
-                                maybeInformListeners(packageName, userId, elapsedRealtime,
-                                        newBucket, false);
-                            }
-                        }
-                    }
-                }
+                checkAndUpdateStandbyState(packageName, userId, pi.applicationInfo.uid,
+                        elapsedRealtime);
             }
         }
         if (DEBUG) {
@@ -516,6 +474,90 @@
         return true;
     }
 
+    /** Check if we need to update the standby state of a specific app. */
+    private void checkAndUpdateStandbyState(String packageName, @UserIdInt int userId,
+            int uid, long elapsedRealtime) {
+        if (uid <= 0) {
+            try {
+                uid = mPackageManager.getPackageUidAsUser(packageName, userId);
+            } catch (PackageManager.NameNotFoundException e) {
+                // Not a valid package for this user, nothing to do
+                // TODO: Remove any history of removed packages
+                return;
+            }
+        }
+        final boolean isSpecial = isAppSpecial(packageName,
+                UserHandle.getAppId(uid),
+                userId);
+        if (DEBUG) {
+            Slog.d(TAG, "   Checking idle state for " + packageName + " special=" +
+                    isSpecial);
+        }
+        if (isSpecial) {
+            synchronized (mAppIdleLock) {
+                mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime,
+                        STANDBY_BUCKET_EXEMPTED, REASON_DEFAULT);
+            }
+            maybeInformListeners(packageName, userId, elapsedRealtime,
+                    STANDBY_BUCKET_EXEMPTED, false);
+        } else {
+            synchronized (mAppIdleLock) {
+                final AppIdleHistory.AppUsageHistory app =
+                        mAppIdleHistory.getAppUsageHistory(packageName,
+                        userId, elapsedRealtime);
+                String reason = app.bucketingReason;
+
+                // If the bucket was forced by the user/developer, leave it alone.
+                // A usage event will be the only way to bring it out of this forced state
+                if (REASON_FORCED.equals(app.bucketingReason)) {
+                    return;
+                }
+                final int oldBucket = app.currentBucket;
+                int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED
+                boolean predictionLate = false;
+                // Compute age-based bucket
+                if (REASON_DEFAULT.equals(app.bucketingReason)
+                        || REASON_USAGE.equals(app.bucketingReason)
+                        || REASON_TIMEOUT.equals(app.bucketingReason)
+                        || (predictionLate = predictionTimedOut(app, elapsedRealtime))) {
+                    newBucket = getBucketForLocked(packageName, userId,
+                            elapsedRealtime);
+                    if (DEBUG) {
+                        Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
+                    }
+                    reason = REASON_TIMEOUT;
+                }
+                // Check if the app is within one of the timeouts for forced bucket elevation
+                final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
+                if (newBucket >= STANDBY_BUCKET_ACTIVE
+                        && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
+                    newBucket = STANDBY_BUCKET_ACTIVE;
+                    reason = REASON_USAGE;
+                    if (DEBUG) {
+                        Slog.d(TAG, "    Keeping at ACTIVE due to min timeout");
+                    }
+                } else if (newBucket >= STANDBY_BUCKET_WORKING_SET
+                        && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
+                    newBucket = STANDBY_BUCKET_WORKING_SET;
+                    reason = REASON_USAGE;
+                    if (DEBUG) {
+                        Slog.d(TAG, "    Keeping at WORKING_SET due to min timeout");
+                    }
+                }
+                if (DEBUG) {
+                    Slog.d(TAG, "     Old bucket=" + oldBucket
+                            + ", newBucket=" + newBucket);
+                }
+                if (oldBucket < newBucket || predictionLate) {
+                    mAppIdleHistory.setAppStandbyBucket(packageName, userId,
+                            elapsedRealtime, newBucket, reason);
+                    maybeInformListeners(packageName, userId, elapsedRealtime,
+                            newBucket, false);
+                }
+            }
+        }
+    }
+
     private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) {
         return app.bucketingReason != null
                 && app.bucketingReason.startsWith(REASON_PREDICTED)
@@ -526,7 +568,9 @@
 
     private boolean hasBucketTimeoutPassed(AppIdleHistory.AppUsageHistory app,
             long elapsedRealtime) {
-        return app.bucketTimeoutTime < mAppIdleHistory.getElapsedTime(elapsedRealtime);
+        final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
+        return app.bucketActiveTimeoutTime < elapsedTimeAdjusted
+                && app.bucketWorkingSetTimeoutTime < elapsedTimeAdjusted;
     }
 
     private void maybeInformListeners(String packageName, int userId,
@@ -631,16 +675,22 @@
                         event.mPackage, userId, elapsedRealtime);
                 final int prevBucket = appHistory.currentBucket;
                 final String prevBucketReason = appHistory.bucketingReason;
+                final long nextCheckTime;
                 if (event.mEventType == UsageEvents.Event.NOTIFICATION_SEEN) {
+                    // Mild usage elevates to WORKING_SET but doesn't change usage time.
                     mAppIdleHistory.reportUsage(appHistory, event.mPackage,
                             STANDBY_BUCKET_WORKING_SET,
-                            elapsedRealtime, elapsedRealtime + mNotificationSeenTimeoutMillis);
+                            0, elapsedRealtime + mNotificationSeenTimeoutMillis);
+                    nextCheckTime = mNotificationSeenTimeoutMillis;
                 } else {
-                    mAppIdleHistory.reportUsage(event.mPackage, userId,
+                    mAppIdleHistory.reportUsage(appHistory, event.mPackage,
                             STANDBY_BUCKET_ACTIVE,
                             elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
+                    nextCheckTime = mStrongUsageTimeoutMillis;
                 }
-
+                mHandler.sendMessageDelayed(mHandler.obtainMessage
+                        (MSG_CHECK_PACKAGE_IDLE_STATE, userId, -1, event.mPackage),
+                        nextCheckTime);
                 final boolean userStartedInteracting =
                         appHistory.currentBucket == STANDBY_BUCKET_ACTIVE &&
                         prevBucket != appHistory.currentBucket &&
@@ -932,9 +982,24 @@
 
             // If the bucket is required to stay in a higher state for a specified duration, don't
             // override unless the duration has passed
-            if (predicted && app.currentBucket < newBucket
-                    && !hasBucketTimeoutPassed(app, elapsedRealtime)) {
-                return;
+            if (predicted) {
+                // Check if the app is within one of the timeouts for forced bucket elevation
+                final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
+                if (newBucket > STANDBY_BUCKET_ACTIVE
+                        && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
+                    newBucket = STANDBY_BUCKET_ACTIVE;
+                    reason = REASON_USAGE;
+                    if (DEBUG) {
+                        Slog.d(TAG, "    Keeping at ACTIVE due to min timeout");
+                    }
+                } else if (newBucket > STANDBY_BUCKET_WORKING_SET
+                        && app.bucketWorkingSetTimeoutTime > elapsedTimeAdjusted) {
+                    newBucket = STANDBY_BUCKET_WORKING_SET;
+                    reason = REASON_USAGE;
+                    if (DEBUG) {
+                        Slog.d(TAG, "    Keeping at WORKING_SET due to min timeout");
+                    }
+                }
             }
 
             mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
@@ -1347,6 +1412,10 @@
                             + ", Charging state:" + mCharging);
                     informParoleStateChanged();
                     break;
+                case MSG_CHECK_PACKAGE_IDLE_STATE:
+                    checkAndUpdateStandbyState((String) msg.obj, msg.arg1, msg.arg2,
+                            mInjector.elapsedRealtime());
+                    break;
                 default:
                     super.handleMessage(msg);
                     break;
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;