Merge "[QS] Fix irritating animation for QS"
diff --git a/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java b/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java
new file mode 100644
index 0000000..fc6302e
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/MeasuredTextMemoryUsageTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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 android.text;
+
+import static android.text.TextDirectionHeuristics.LTR;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Typeface;
+import android.text.Layout;
+import android.text.style.TextAppearanceSpan;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.CharBuffer;
+import java.util.Random;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class MeasuredTextMemoryUsageTest {
+    private static final int WORD_LENGTH = 9;  // Random word has 9 characters.
+    private static final boolean NO_STYLE_TEXT = false;
+
+    private static TextPaint PAINT = new TextPaint();
+
+    private static int TRIAL_COUNT = 100;
+
+    public MeasuredTextMemoryUsageTest() {}
+
+    private TextPerfUtils mTextUtil = new TextPerfUtils();
+
+    @Before
+    public void setUp() {
+        mTextUtil.resetRandom(0 /* seed */);
+    }
+
+    private void reportMemoryUsage(int memoryUsage, String key) {
+        Bundle status = new Bundle();
+        status.putInt(key + "_median", memoryUsage);
+        InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, status);
+    }
+
+    private int median(int[] values) {
+        return values.length % 2 == 0 ?
+                (values[values.length / 2] + values[values.length / 2 - 1]) / 2:
+                values[values.length / 2];
+    }
+
+    @Test
+    public void testMemoryUsage_NoHyphenation() {
+        int[] memories = new int[TRIAL_COUNT];
+        // 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().getMemoryUsage();
+        }
+        reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation");
+    }
+
+    @Test
+    public void testMemoryUsage_Hyphenation() {
+        int[] memories = new int[TRIAL_COUNT];
+        // 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().getMemoryUsage();
+        }
+        reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation");
+    }
+
+    @Test
+    public void testMemoryUsage_NoHyphenation_WidthOnly() {
+        int[] memories = new int[TRIAL_COUNT];
+        // 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(false /* width only */).getMemoryUsage();
+        }
+        reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation_WidthOnly");
+    }
+
+    @Test
+    public void testMemoryUsage_Hyphenatation_WidthOnly() {
+        int[] memories = new int[TRIAL_COUNT];
+        // 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(false /* width only */).getMemoryUsage();
+        }
+        reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation_WidthOnly");
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java b/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java
new file mode 100644
index 0000000..98f2bd5
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/MeasuredTextPerfTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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 android.text;
+
+import static android.text.TextDirectionHeuristics.LTR;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Typeface;
+import android.text.Layout;
+import android.text.style.TextAppearanceSpan;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.CharBuffer;
+import java.util.Random;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+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;
+    private static final boolean STYLE_TEXT = true;
+
+    private static TextPaint PAINT = new TextPaint();
+    private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
+
+    public MeasuredTextPerfTest() {}
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private TextPerfUtils mTextUtil = new TextPerfUtils();
+
+    @Before
+    public void setUp() {
+        mTextUtil.resetRandom(0 /* seed */);
+    }
+
+    @Test
+    public void testCreate_NoStyled_Hyphenation() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
+            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();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
+            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();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
+            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();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            state.resumeTiming();
+
+            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();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            state.resumeTiming();
+
+            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();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            state.resumeTiming();
+
+            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();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            state.resumeTiming();
+
+            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();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            state.resumeTiming();
+
+            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 682885b..bab2a85 100644
--- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java
@@ -43,74 +43,30 @@
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class StaticLayoutPerfTest {
+    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;
+    private static final boolean STYLE_TEXT = true;
+
+    private static TextPaint PAINT = new TextPaint();
+    private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
 
     public StaticLayoutPerfTest() {}
 
     @Rule
     public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    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 int PARA_LENGTH = 500;  // Number of characters in a paragraph.
-
-    private static final boolean NO_STYLE_TEXT = false;
-    private static final boolean STYLE_TEXT = true;
-
-    private Random mRandom;
-
-    private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-    private static final int ALPHABET_LENGTH = ALPHABET.length();
-
-    private static final ColorStateList TEXT_COLOR = ColorStateList.valueOf(0x00000000);
-    private static final String[] FAMILIES = { "sans-serif", "serif", "monospace" };
-    private static final int[] STYLES = {
-            Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC
-    };
-
-    private final char[] mBuffer = new char[PARA_LENGTH];
-
-    private static TextPaint PAINT = new TextPaint();
-    private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
-
-    private CharSequence generateRandomParagraph(int wordLen, boolean applyRandomStyle) {
-        for (int i = 0; i < PARA_LENGTH; i++) {
-            if (i % (wordLen + 1) == wordLen) {
-                mBuffer[i] = ' ';
-            } else {
-                mBuffer[i] = ALPHABET.charAt(mRandom.nextInt(ALPHABET_LENGTH));
-            }
-        }
-
-        CharSequence cs = CharBuffer.wrap(mBuffer);
-        if (!applyRandomStyle) {
-            return cs;
-        }
-
-        SpannableStringBuilder ssb = new SpannableStringBuilder(cs);
-        for (int i = 0; i < ssb.length(); i += WORD_LENGTH) {
-            final int spanStart = i;
-            final int spanEnd = (i + WORD_LENGTH) > ssb.length() ? ssb.length() : i + WORD_LENGTH;
-
-            final TextAppearanceSpan span = new TextAppearanceSpan(
-                  FAMILIES[mRandom.nextInt(FAMILIES.length)],
-                  STYLES[mRandom.nextInt(STYLES.length)],
-                  24 + mRandom.nextInt(32),  // text size. min 24 max 56
-                  TEXT_COLOR, TEXT_COLOR);
-
-            ssb.setSpan(span, spanStart, spanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
-        }
-        return ssb;
-    }
+    private TextPerfUtils mTextUtil = new TextPerfUtils();
 
     @Before
     public void setUp() {
-        mRandom = new Random(0);
+        mTextUtil.resetRandom(0 /* seed */);
     }
 
     @Test
     public void testCreate_FixedText_NoStyle_Greedy_NoHyphenation() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+        final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
         while (state.keepRunning()) {
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
@@ -124,7 +80,7 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -139,7 +95,7 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -154,7 +110,7 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -169,7 +125,7 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             state.resumeTiming();
 
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -184,7 +140,7 @@
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             state.resumeTiming();
 
             StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -200,7 +156,7 @@
         while (state.keepRunning()) {
             state.pauseTiming();
             final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
                     .build();
@@ -219,7 +175,7 @@
         while (state.keepRunning()) {
             state.pauseTiming();
             final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                     .build();
@@ -238,7 +194,7 @@
         while (state.keepRunning()) {
             state.pauseTiming();
             final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
                     .build();
@@ -257,7 +213,7 @@
         while (state.keepRunning()) {
             state.pauseTiming();
             final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                     .build();
@@ -276,7 +232,7 @@
         while (state.keepRunning()) {
             state.pauseTiming();
             final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                     .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                     .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
                     .build();
@@ -292,7 +248,7 @@
     @Test
     public void testDraw_FixedText_NoStyled() {
         final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+        final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
@@ -311,7 +267,7 @@
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
@@ -327,7 +283,7 @@
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
@@ -343,7 +299,7 @@
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
@@ -360,7 +316,7 @@
         final RenderNode node = RenderNode.create("benchmark", null);
         while (state.keepRunning()) {
             state.pauseTiming();
-            final CharSequence text = generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
+            final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
@@ -378,7 +334,7 @@
         while (state.keepRunning()) {
             state.pauseTiming();
             final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
@@ -395,7 +351,7 @@
         while (state.keepRunning()) {
             state.pauseTiming();
             final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
@@ -412,7 +368,7 @@
         while (state.keepRunning()) {
             state.pauseTiming();
             final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT).build();
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
@@ -430,7 +386,7 @@
         while (state.keepRunning()) {
             state.pauseTiming();
             final MeasuredText text = new MeasuredText.Builder(
-                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
+                    mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT).build();
             final StaticLayout layout =
                     StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
             final DisplayListCanvas c = node.start(1200, 200);
diff --git a/apct-tests/perftests/core/src/android/text/TextPerfUtils.java b/apct-tests/perftests/core/src/android/text/TextPerfUtils.java
new file mode 100644
index 0000000..dccb34b
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/TextPerfUtils.java
@@ -0,0 +1,93 @@
+/*
+ * 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 android.text;
+
+import static android.text.TextDirectionHeuristics.LTR;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Typeface;
+import android.text.Layout;
+import android.text.style.TextAppearanceSpan;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.CharBuffer;
+import java.util.Random;
+
+public class TextPerfUtils {
+
+    private static final int PARA_LENGTH = 500;  // Number of characters in a paragraph.
+
+    private Random mRandom = new Random(0);
+
+    private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+    private static final int ALPHABET_LENGTH = ALPHABET.length();
+
+    private static final ColorStateList TEXT_COLOR = ColorStateList.valueOf(0x00000000);
+    private static final String[] FAMILIES = { "sans-serif", "serif", "monospace" };
+    private static final int[] STYLES = {
+            Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC
+    };
+
+    private final char[] mBuffer = new char[PARA_LENGTH];
+
+    public void resetRandom(long seed) {
+        mRandom = new Random(seed);
+    }
+
+    public CharSequence nextRandomParagraph(int wordLen, boolean applyRandomStyle) {
+        for (int i = 0; i < PARA_LENGTH; i++) {
+            if (i % (wordLen + 1) == wordLen) {
+                mBuffer[i] = ' ';
+            } else {
+                mBuffer[i] = ALPHABET.charAt(mRandom.nextInt(ALPHABET_LENGTH));
+            }
+        }
+
+        CharSequence cs = CharBuffer.wrap(mBuffer);
+        if (!applyRandomStyle) {
+            return cs;
+        }
+
+        SpannableStringBuilder ssb = new SpannableStringBuilder(cs);
+        for (int i = 0; i < ssb.length(); i += wordLen) {
+            final int spanStart = i;
+            final int spanEnd = (i + wordLen) > ssb.length() ? ssb.length() : i + wordLen;
+
+            final TextAppearanceSpan span = new TextAppearanceSpan(
+                  FAMILIES[mRandom.nextInt(FAMILIES.length)],
+                  STYLES[mRandom.nextInt(STYLES.length)],
+                  24 + mRandom.nextInt(32),  // text size. min 24 max 56
+                  TEXT_COLOR, TEXT_COLOR);
+
+            ssb.setSpan(span, spanStart, spanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        }
+        return ssb;
+    }
+}
diff --git a/api/current.txt b/api/current.txt
index 7185c99..e774f07 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -38296,6 +38296,7 @@
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isStrongBoxBacked();
     method public boolean isTrustedUserPresenceRequired();
+    method public boolean isUnlockedDeviceRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
   }
@@ -38322,6 +38323,7 @@
     method public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setTrustedUserPresenceRequired(boolean);
+    method public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(int);
@@ -38411,6 +38413,8 @@
     method public boolean isDigestsSpecified();
     method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isRandomizedEncryptionRequired();
+    method public boolean isTrustedUserPresenceRequired();
+    method public boolean isUnlockedDeviceRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
   }
@@ -38428,6 +38432,8 @@
     method public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date);
     method public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
+    method public android.security.keystore.KeyProtection.Builder setTrustedUserPresenceRequired(boolean);
+    method public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index ed3897c..396ff1a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2603,12 +2603,17 @@
     method public void onStatusChange();
   }
 
+  public static abstract class AudioPolicy.AudioPolicyVolumeCallback {
+    method public void onVolumeAdjustment(int);
+  }
+
   public static class AudioPolicy.Builder {
     ctor public AudioPolicy.Builder(android.content.Context);
     method public android.media.audiopolicy.AudioPolicy.Builder addMix(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException;
     method public android.media.audiopolicy.AudioPolicy build();
     method public void setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener);
     method public void setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener);
+    method public android.media.audiopolicy.AudioPolicy.Builder setAudioPolicyVolumeCallback(android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback);
     method public android.media.audiopolicy.AudioPolicy.Builder setIsAudioFocusPolicy(boolean);
     method public android.media.audiopolicy.AudioPolicy.Builder setLooper(android.os.Looper) throws java.lang.IllegalArgumentException;
   }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 27fa672..b32af02 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -1214,8 +1214,14 @@
     // # of major page-faults
     optional int64 pgmajfault = 5;
 
-    // RSS+CACHE(+SWAP)
-    optional int64 usage_in_bytes = 6;
+    // RSS
+    optional int64 rss_in_bytes = 6;
+
+    // CACHE
+    optional int64 cache_in_bytes = 7;
+
+    // SWAP
+    optional int64 swap_in_bytes = 8;
 }
 
 /*
@@ -1237,8 +1243,14 @@
     // # of major page-faults
     optional int64 pgmajfault = 5;
 
-    // RSS+CACHE(+SWAP)
-    optional int64 usage_in_bytes = 6;
+    // RSS
+    optional int64 rss_in_bytes = 6;
+
+    // CACHE
+    optional int64 cache_in_bytes = 7;
+
+    // SWAP
+    optional int64 swap_in_bytes = 8;
 }
 
 /*
@@ -1277,8 +1289,14 @@
     // # of major page-faults
     optional int64 pgmajfault = 5;
 
-    // RSS+CACHE(+SWAP)
-    optional int64 usage_in_bytes = 6;
+    // RSS
+    optional int64 rss_in_bytes = 6;
+
+    // CACHE
+    optional int64 cache_in_bytes = 7;
+
+    // SWAP
+    optional int64 swap_in_bytes = 8;
 }
 
 /*
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index fd7ad88..e7d0c8b 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -17,7 +17,6 @@
 package android.app;
 
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
-
 import static java.lang.Character.MIN_VALUE;
 
 import android.annotation.CallSuper;
@@ -4713,7 +4712,6 @@
      * their launch had come from the original activity.
      * @param intent The Intent to start.
      * @param options ActivityOptions or null.
-     * @param permissionToken Token received from the system that permits this call to be made.
      * @param ignoreTargetSecurity If true, the activity manager will not check whether the
      * caller it is doing the start is, is actually allowed to start the target activity.
      * If you set this to true, you must set an explicit component in the Intent and do any
@@ -4722,7 +4720,7 @@
      * @hide
      */
     public void startActivityAsCaller(Intent intent, @Nullable Bundle options,
-            IBinder permissionToken, boolean ignoreTargetSecurity, int userId) {
+            boolean ignoreTargetSecurity, int userId) {
         if (mParent != null) {
             throw new RuntimeException("Can't be called from a child");
         }
@@ -4730,7 +4728,7 @@
         Instrumentation.ActivityResult ar =
                 mInstrumentation.execStartActivityAsCaller(
                         this, mMainThread.getApplicationThread(), mToken, this,
-                        intent, -1, options, permissionToken, ignoreTargetSecurity, userId);
+                        intent, -1, options, ignoreTargetSecurity, userId);
         if (ar != null) {
             mMainThread.sendActivityResult(
                 mToken, mEmbeddedID, -1, ar.getResultCode(),
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 4d5ac6f..e2ce8b1 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -449,31 +449,6 @@
      */
     public static final int INTENT_SENDER_FOREGROUND_SERVICE = 5;
 
-    /**
-     * Extra included on intents that are delegating the call to
-     * ActivityManager#startActivityAsCaller to another app.  This token is necessary for that call
-     * to succeed.  Type is IBinder.
-     * @hide
-     */
-    public static final String EXTRA_PERMISSION_TOKEN = "android.app.extra.PERMISSION_TOKEN";
-
-    /**
-     * Extra included on intents that contain an EXTRA_INTENT, with options that the contained
-     * intent may want to be started with.  Type is Bundle.
-     * TODO: remove once the ChooserActivity moves to systemui
-     * @hide
-     */
-    public static final String EXTRA_OPTIONS = "android.app.extra.OPTIONS";
-
-    /**
-     * Extra included on intents that contain an EXTRA_INTENT, use this boolean value for the
-     * parameter of the same name when starting the contained intent.
-     * TODO: remove once the ChooserActivity moves to systemui
-     * @hide
-     */
-    public static final String EXTRA_IGNORE_TARGET_SECURITY =
-            "android.app.extra.EXTRA_IGNORE_TARGET_SECURITY";
-
     /** @hide User operation call: success! */
     public static final int USER_OP_SUCCESS = 0;
 
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 6dcecf1..02be002 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -456,11 +456,10 @@
     boolean isTopOfTask(in IBinder token);
     void notifyLaunchTaskBehindComplete(in IBinder token);
     void notifyEnterAnimationComplete(in IBinder token);
-    IBinder requestStartActivityPermissionToken(in IBinder delegatorToken);
     int startActivityAsCaller(in IApplicationThread caller, in String callingPackage,
             in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
             int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options,
-            in IBinder permissionToken, boolean ignoreTargetSecurity, int userId);
+            boolean ignoreTargetSecurity, int userId);
     int addAppTask(in IBinder activityToken, in Intent intent,
             in ActivityManager.TaskDescription description, in Bitmap thumbnail);
     Point getAppTaskThumbnailSize();
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index f90b276..198bce6 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1879,8 +1879,8 @@
      */
     public ActivityResult execStartActivityAsCaller(
             Context who, IBinder contextThread, IBinder token, Activity target,
-            Intent intent, int requestCode, Bundle options, IBinder permissionToken,
-            boolean ignoreTargetSecurity, int userId) {
+            Intent intent, int requestCode, Bundle options, boolean ignoreTargetSecurity,
+            int userId) {
         IApplicationThread whoThread = (IApplicationThread) contextThread;
         if (mActivityMonitors != null) {
             synchronized (mSync) {
@@ -1911,8 +1911,7 @@
                 .startActivityAsCaller(whoThread, who.getBasePackageName(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
                         token, target != null ? target.mEmbeddedID : null,
-                        requestCode, 0, null, options, permissionToken,
-                        ignoreTargetSecurity, userId);
+                        requestCode, 0, null, options, ignoreTargetSecurity, userId);
             checkStartActivityResult(result, intent);
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3b4511e..131abb5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8261,7 +8261,7 @@
     }
 
     /**
-     * Called by a device or profile owner to restrict packages from accessing metered data.
+     * Called by a device or profile owner to restrict packages from using metered data.
      *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with.
      * @param packageNames the list of package names to be restricted.
@@ -8283,7 +8283,7 @@
 
     /**
      * Called by a device or profile owner to retrieve the list of packages which are restricted
-     * by the admin from accessing metered data.
+     * by the admin from using metered data.
      *
      * @param admin which {@link DeviceAdminReceiver} this request is associated with.
      * @return the list of restricted package names.
@@ -8302,6 +8302,30 @@
     }
 
     /**
+     * Called by the system to check if a package is restricted from using metered data
+     * by {@param admin}.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName the package whose restricted status is needed.
+     * @param userId the user to which {@param packageName} belongs.
+     * @return {@code true} if the package is restricted by admin, otherwise {@code false}
+     * @throws SecurityException if the caller doesn't run with {@link Process#SYSTEM_UID}
+     * @hide
+     */
+    public boolean isMeteredDataDisabledForUser(@NonNull ComponentName admin, String packageName,
+            @UserIdInt int userId) {
+        throwIfParentInstance("getMeteredDataDisabledForUser");
+        if (mService != null) {
+            try {
+                return mService.isMeteredDataDisabledForUser(admin, packageName, userId);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+        return false;
+    }
+
+    /**
      * Called by device owners to retrieve device logs from before the device's last reboot.
      * <p>
      * <strong> This API is not supported on all devices. Calling this API on unsupported devices
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 7fc31b1..cba9311 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -413,4 +413,6 @@
     List<ApnSetting> getOverrideApns(in ComponentName admin);
     void setOverrideApnsEnabled(in ComponentName admin, boolean enabled);
     boolean isOverrideApnEnabled(in ComponentName admin);
+
+    boolean isMeteredDataDisabledForUser(in ComponentName admin, String packageName, int userId);
 }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 7b27fc0..b3c8737 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -7057,7 +7057,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if none was found.
      *
      * @deprecated
@@ -7075,7 +7075,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or the default value if none was found.
      *
      * @see #putExtra(String, boolean)
@@ -7092,7 +7092,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or the default value if none was found.
      *
      * @see #putExtra(String, byte)
@@ -7109,7 +7109,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or the default value if none was found.
      *
      * @see #putExtra(String, short)
@@ -7126,7 +7126,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or the default value if none was found.
      *
      * @see #putExtra(String, char)
@@ -7143,7 +7143,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or the default value if none was found.
      *
      * @see #putExtra(String, int)
@@ -7160,7 +7160,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or the default value if none was found.
      *
      * @see #putExtra(String, long)
@@ -7177,7 +7177,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra(),
+     * @return the value of an item previously added with putExtra(),
      * or the default value if no such item is present
      *
      * @see #putExtra(String, float)
@@ -7194,7 +7194,7 @@
      * @param defaultValue the value to be returned if no value of the desired
      * type is stored with the given name.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or the default value if none was found.
      *
      * @see #putExtra(String, double)
@@ -7209,7 +7209,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no String value was found.
      *
      * @see #putExtra(String, String)
@@ -7223,7 +7223,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no CharSequence value was found.
      *
      * @see #putExtra(String, CharSequence)
@@ -7237,7 +7237,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no Parcelable value was found.
      *
      * @see #putExtra(String, Parcelable)
@@ -7251,7 +7251,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no Parcelable[] value was found.
      *
      * @see #putExtra(String, Parcelable[])
@@ -7265,8 +7265,9 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
-     * or null if no ArrayList<Parcelable> value was found.
+     * @return the value of an item previously added with
+     * putParcelableArrayListExtra(), or null if no
+     * ArrayList<Parcelable> value was found.
      *
      * @see #putParcelableArrayListExtra(String, ArrayList)
      */
@@ -7279,7 +7280,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no Serializable value was found.
      *
      * @see #putExtra(String, Serializable)
@@ -7293,8 +7294,9 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
-     * or null if no ArrayList<Integer> value was found.
+     * @return the value of an item previously added with
+     * putIntegerArrayListExtra(), or null if no
+     * ArrayList<Integer> value was found.
      *
      * @see #putIntegerArrayListExtra(String, ArrayList)
      */
@@ -7307,8 +7309,9 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
-     * or null if no ArrayList<String> value was found.
+     * @return the value of an item previously added with
+     * putStringArrayListExtra(), or null if no
+     * ArrayList<String> value was found.
      *
      * @see #putStringArrayListExtra(String, ArrayList)
      */
@@ -7321,8 +7324,9 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
-     * or null if no ArrayList<CharSequence> value was found.
+     * @return the value of an item previously added with
+     * putCharSequenceArrayListExtra, or null if no
+     * ArrayList<CharSequence> value was found.
      *
      * @see #putCharSequenceArrayListExtra(String, ArrayList)
      */
@@ -7335,7 +7339,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no boolean array value was found.
      *
      * @see #putExtra(String, boolean[])
@@ -7349,7 +7353,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no byte array value was found.
      *
      * @see #putExtra(String, byte[])
@@ -7363,7 +7367,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no short array value was found.
      *
      * @see #putExtra(String, short[])
@@ -7377,7 +7381,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no char array value was found.
      *
      * @see #putExtra(String, char[])
@@ -7391,7 +7395,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no int array value was found.
      *
      * @see #putExtra(String, int[])
@@ -7405,7 +7409,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no long array value was found.
      *
      * @see #putExtra(String, long[])
@@ -7419,7 +7423,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no float array value was found.
      *
      * @see #putExtra(String, float[])
@@ -7433,7 +7437,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no double array value was found.
      *
      * @see #putExtra(String, double[])
@@ -7447,7 +7451,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no String array value was found.
      *
      * @see #putExtra(String, String[])
@@ -7461,7 +7465,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no CharSequence array value was found.
      *
      * @see #putExtra(String, CharSequence[])
@@ -7475,7 +7479,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no Bundle value was found.
      *
      * @see #putExtra(String, Bundle)
@@ -7489,7 +7493,7 @@
      *
      * @param name The name of the desired item.
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or null if no IBinder value was found.
      *
      * @see #putExtra(String, IBinder)
@@ -7509,7 +7513,7 @@
      * @param defaultValue The default value to return in case no item is
      * associated with the key 'name'
      *
-     * @return the value of an item that previously added with putExtra()
+     * @return the value of an item previously added with putExtra(),
      * or defaultValue if none was found.
      *
      * @see #putExtra
diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java
index f84ec65..a748f4d 100644
--- a/core/java/android/database/CursorWindow.java
+++ b/core/java/android/database/CursorWindow.java
@@ -28,6 +28,7 @@
 import android.util.LongSparseArray;
 import android.util.SparseIntArray;
 
+import dalvik.annotation.optimization.FastNative;
 import dalvik.system.CloseGuard;
 
 /**
@@ -62,28 +63,43 @@
     private static native void nativeDispose(long windowPtr);
     private static native void nativeWriteToParcel(long windowPtr, Parcel parcel);
 
-    private static native void nativeClear(long windowPtr);
-
-    private static native int nativeGetNumRows(long windowPtr);
-    private static native boolean nativeSetNumColumns(long windowPtr, int columnNum);
-    private static native boolean nativeAllocRow(long windowPtr);
-    private static native void nativeFreeLastRow(long windowPtr);
-
-    private static native int nativeGetType(long windowPtr, int row, int column);
+    private static native String nativeGetName(long windowPtr);
     private static native byte[] nativeGetBlob(long windowPtr, int row, int column);
     private static native String nativeGetString(long windowPtr, int row, int column);
-    private static native long nativeGetLong(long windowPtr, int row, int column);
-    private static native double nativeGetDouble(long windowPtr, int row, int column);
     private static native void nativeCopyStringToBuffer(long windowPtr, int row, int column,
             CharArrayBuffer buffer);
-
     private static native boolean nativePutBlob(long windowPtr, byte[] value, int row, int column);
-    private static native boolean nativePutString(long windowPtr, String value, int row, int column);
+    private static native boolean nativePutString(long windowPtr, String value,
+            int row, int column);
+
+    // Below native methods don't do unconstrained work, so are FastNative for performance
+
+    @FastNative
+    private static native void nativeClear(long windowPtr);
+
+    @FastNative
+    private static native int nativeGetNumRows(long windowPtr);
+    @FastNative
+    private static native boolean nativeSetNumColumns(long windowPtr, int columnNum);
+    @FastNative
+    private static native boolean nativeAllocRow(long windowPtr);
+    @FastNative
+    private static native void nativeFreeLastRow(long windowPtr);
+
+    @FastNative
+    private static native int nativeGetType(long windowPtr, int row, int column);
+    @FastNative
+    private static native long nativeGetLong(long windowPtr, int row, int column);
+    @FastNative
+    private static native double nativeGetDouble(long windowPtr, int row, int column);
+
+    @FastNative
     private static native boolean nativePutLong(long windowPtr, long value, int row, int column);
+    @FastNative
     private static native boolean nativePutDouble(long windowPtr, double value, int row, int column);
+    @FastNative
     private static native boolean nativePutNull(long windowPtr, int row, int column);
 
-    private static native String nativeGetName(long windowPtr);
 
     /**
      * Creates a new empty cursor window and gives it a name.
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 7fb0c89..7297426 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -22,7 +22,9 @@
 
 /**
  * Class representing a sensor. Use {@link SensorManager#getSensorList} to get
- * the list of available Sensors.
+ * the list of available sensors. For more information about Android sensors,
+ * read the
+ * <a href="/guide/topics/sensors/sensors_motion.html">Motion Sensors guide</a>.</p>
  *
  * @see SensorManager
  * @see SensorEventListener
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 13b9206..2306e5f 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -61,6 +61,7 @@
  * @attr ref android.R.styleable#KeyboardView_keyBackground
  * @attr ref android.R.styleable#KeyboardView_keyPreviewLayout
  * @attr ref android.R.styleable#KeyboardView_keyPreviewOffset
+ * @attr ref android.R.styleable#KeyboardView_keyPreviewHeight
  * @attr ref android.R.styleable#KeyboardView_labelTextSize
  * @attr ref android.R.styleable#KeyboardView_keyTextSize
  * @attr ref android.R.styleable#KeyboardView_keyTextColor
diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java
index f3a95b9..a86237d 100644
--- a/core/java/android/os/BatteryManagerInternal.java
+++ b/core/java/android/os/BatteryManagerInternal.java
@@ -24,26 +24,63 @@
 public abstract class BatteryManagerInternal {
     /**
      * Returns true if the device is plugged into any of the specified plug types.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
      */
     public abstract boolean isPowered(int plugTypeSet);
 
     /**
      * Returns the current plug type.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
      */
     public abstract int getPlugType();
 
     /**
      * Returns battery level as a percentage.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
      */
     public abstract int getBatteryLevel();
 
     /**
+     * Instantaneous battery capacity in uA-h, as defined in the HealthInfo HAL struct.
+     * Please note apparently it could be bigger than {@link #getBatteryFullCharge}.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
+     *
+     * @see android.hardware.health.V1_0.HealthInfo#batteryChargeCounter
+     */
+    public abstract int getBatteryChargeCounter();
+
+    /**
+     * Battery charge value when it is considered to be "full" in uA-h , as defined in the
+     * HealthInfo HAL struct.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
+     *
+     * @see android.hardware.health.V1_0.HealthInfo#batteryFullCharge
+     */
+    public abstract int getBatteryFullCharge();
+
+    /**
      * Returns whether we currently consider the battery level to be low.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
      */
     public abstract boolean getBatteryLevelLow();
 
     /**
      * Returns a non-zero value if an unsupported charger is attached.
+     *
+     * This is a simple accessor that's safe to be called from any locks, but internally it may
+     * wait on the battery service lock.
      */
     public abstract int getInvalidCharger();
 }
diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl
index 738eb68..c4b7715 100644
--- a/core/java/android/security/IKeystoreService.aidl
+++ b/core/java/android/security/IKeystoreService.aidl
@@ -71,7 +71,7 @@
         in byte[] entropy);
     int abort(IBinder handle);
     boolean isOperationAuthorized(IBinder token);
-    int addAuthToken(in byte[] authToken);
+    int addAuthToken(in byte[] authToken, in int androidId);
     int onUserAdded(int userId, int parentId);
     int onUserRemoved(int userId);
     int attestKey(String alias, in KeymasterArguments params, out KeymasterCertificateChain chain);
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 3464370..479231d 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -74,6 +74,7 @@
     public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505;
     public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
     public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507;
+    public static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = KM_BOOL | 509;
 
     public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
     public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
@@ -215,6 +216,7 @@
     public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH = -58;
     public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59;
     public static final int KM_ERROR_CANNOT_ATTEST_IDS = -66;
+    public static final int KM_ERROR_DEVICE_LOCKED = -72;
     public static final int KM_ERROR_UNIMPLEMENTED = -100;
     public static final int KM_ERROR_VERSION_MISMATCH = -101;
     public static final int KM_ERROR_UNKNOWN_ERROR = -1000;
@@ -261,6 +263,7 @@
         sErrorCodeToString.put(KM_ERROR_INVALID_MAC_LENGTH,
                 "Invalid MAC or authentication tag length");
         sErrorCodeToString.put(KM_ERROR_CANNOT_ATTEST_IDS, "Unable to attest device ids");
+        sErrorCodeToString.put(KM_ERROR_DEVICE_LOCKED, "Device locked");
         sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented");
         sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error");
     }
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 45fbf6f..aafcf44 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -672,6 +672,13 @@
         return width;
     }
 
+    /**
+     * This only works if the MeasuredParagraph is computed with buildForStaticLayout.
+     */
+    @IntRange(from = 0) int getMemoryUsage() {
+        return nGetMemoryUsage(mNativePtr);
+    }
+
     private static native /* Non Zero */ long nInitBuilder();
 
     /**
@@ -718,4 +725,7 @@
 
     @CriticalNative
     private static native /* Non Zero */ long nGetReleaseFunc();
+
+    @CriticalNative
+    private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
 }
diff --git a/core/java/android/text/MeasuredText.java b/core/java/android/text/MeasuredText.java
index ff23395..bb7a9e0 100644
--- a/core/java/android/text/MeasuredText.java
+++ b/core/java/android/text/MeasuredText.java
@@ -333,6 +333,20 @@
         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
     //
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 25a177e..8af4b71 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -42,7 +42,6 @@
         DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
         DEFAULT_FLAGS.put("settings_battery_v2", "true");
         DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
-        DEFAULT_FLAGS.put("settings_security_settings_v2", "true");
         DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
         DEFAULT_FLAGS.put("settings_suggestion_ui_v2", "false");
         DEFAULT_FLAGS.put("settings_about_phone_v2", "false");
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index 84d1850..a4da05f 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -26,7 +26,6 @@
 import android.media.update.ViewProvider;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
-import android.view.View;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -41,10 +40,10 @@
  * adds it to the view.
  * 2) Initialize MediaControlView2 programmatically and add it to a ViewGroup instance.
  *
- * In the first option, VideoView2 automatically connects MediaControlView2 to MediaController2,
+ * In the first option, VideoView2 automatically connects MediaControlView2 to MediaController,
  * which is necessary to communicate with MediaSession2. In the second option, however, the
- * developer needs to manually retrieve a MediaController2 instance and set it to MediaControlView2
- * by calling setController(MediaController2 controller).
+ * developer needs to manually retrieve a MediaController instance and set it to MediaControlView2
+ * by calling setController(MediaController controller).
  *
  * TODO PUBLIC API
  * @hide
@@ -67,18 +66,67 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface Button {}
 
+    /**
+     * MediaControlView2 button value for playing and pausing media.
+     */
     public static final int BUTTON_PLAY_PAUSE = 1;
+    /**
+     * MediaControlView2 button value for jumping 30 seconds forward.
+     */
     public static final int BUTTON_FFWD = 2;
+    /**
+     * MediaControlView2 button value for jumping 10 seconds backward.
+     */
     public static final int BUTTON_REW = 3;
+    /**
+     * MediaControlView2 button value for jumping to next media.
+     */
     public static final int BUTTON_NEXT = 4;
+    /**
+     * MediaControlView2 button value for jumping to previous media.
+     */
     public static final int BUTTON_PREV = 5;
+    /**
+     * MediaControlView2 button value for showing/hiding subtitle track.
+     */
     public static final int BUTTON_SUBTITLE = 6;
+    /**
+     * MediaControlView2 button value for toggling full screen.
+     */
     public static final int BUTTON_FULL_SCREEN = 7;
+    /**
+     * MediaControlView2 button value for showing/hiding overflow buttons.
+     */
     public static final int BUTTON_OVERFLOW = 8;
+    /**
+     * MediaControlView2 button value for muting audio.
+     */
     public static final int BUTTON_MUTE = 9;
+    /**
+     * MediaControlView2 button value for adjusting aspect ratio of view.
+     */
     public static final int BUTTON_ASPECT_RATIO = 10;
+    /**
+     * MediaControlView2 button value for showing/hiding settings page.
+     */
     public static final int BUTTON_SETTINGS = 11;
 
+    /**
+     * String for receiving command to show subtitle from MediaSession. Can be checked by
+     * implementing {@link android.media.session.MediaSession.Callback#onCommand}
+     */
+    public static final String COMMAND_SHOW_SUBTITLE = "showSubtitle";
+    /**
+     * String for receiving command to hide subtitle from MediaSession. Can be checked by
+     * implementing {@link android.media.session.MediaSession.Callback#onCommand}
+     */
+    public static final String COMMAND_HIDE_SUBTITLE = "hideSubtitle";
+    /**
+     * String for receiving command to set fullscreen from MediaSession. Can be checked by
+     * implementing {@link android.media.session.MediaSession.Callback#onCommand}
+     */
+    public static final String COMMAND_SET_FULLSCREEN = "setFullscreen";
+
     private final MediaControlView2Provider mProvider;
 
     public MediaControlView2(@NonNull Context context) {
@@ -90,12 +138,12 @@
     }
 
     public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs,
-                            int defStyleAttr) {
+            int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
     public MediaControlView2(@NonNull Context context, @Nullable AttributeSet attrs,
-                            int defStyleAttr, int defStyleRes) {
+            int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
         mProvider = ApiLoader.getProvider(context)
@@ -110,7 +158,7 @@
     }
 
     /**
-     * Sets MediaController2 instance to control corresponding MediaSession2.
+     * Sets MediaController instance to control corresponding MediaSession.
      */
     public void setController(MediaController controller) {
         mProvider.setController_impl(controller);
@@ -128,7 +176,7 @@
      * Shows the control view on screen. It will disappear automatically after {@code timeout}
      * milliseconds of inactivity.
      */
-    public void show(int timeout) {
+    public void show(long timeout) {
         mProvider.show_impl(timeout);
     }
 
@@ -147,41 +195,26 @@
     }
 
     /**
-     * If the media selected has a subtitle track, calling this method will display the subtitle at
-     * the bottom of the view. If a media has multiple subtitle tracks, this method will select the
-     * first one of them.
-     */
-    public void showSubtitle() {
-        mProvider.showSubtitle_impl();
-    }
-
-    /**
-     * Hides the currently displayed subtitle.
-     */
-    public void hideSubtitle() {
-        mProvider.hideSubtitle_impl();
-    }
-
-    /**
-     * Set listeners for previous and next buttons to customize the behavior of clicking them.
-     * The UI for these buttons are provided as default and will be automatically displayed when
-     * this method is called.
+     * Changes the visibility state of an individual button. Default value is View.Visible.
      *
-     * @param next Listener for clicking next button
-     * @param prev Listener for clicking previous button
+     * @param button the {@code Button} assigned to individual buttons
+     * <ul>
+     * <li>{@link #BUTTON_PLAY_PAUSE}
+     * <li>{@link #BUTTON_FFWD}
+     * <li>{@link #BUTTON_REW}
+     * <li>{@link #BUTTON_NEXT}
+     * <li>{@link #BUTTON_PREV}
+     * <li>{@link #BUTTON_SUBTITLE}
+     * <li>{@link #BUTTON_FULL_SCREEN}
+     * <li>{@link #BUTTON_MUTE}
+     * <li>{@link #BUTTON_OVERFLOW}
+     * <li>{@link #BUTTON_ASPECT_RATIO}
+     * <li>{@link #BUTTON_SETTINGS}
+     * </ul>
+     * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
      */
-    public void setPrevNextListeners(View.OnClickListener next, View.OnClickListener prev) {
-        mProvider.setPrevNextListeners_impl(next, prev);
-    }
-
-    /**
-     * Hides the specified button from view.
-     *
-     * @param button the constant integer assigned to individual buttons
-     * @param visible whether the button should be visible or not
-     */
-    public void setButtonVisibility(int button, boolean visible) {
-        mProvider.setButtonVisibility_impl(button, visible);
+    public void setButtonVisibility(@Button int button, @Visibility int visibility) {
+        mProvider.setButtonVisibility_impl(button, visibility);
     }
 
     @Override
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 997d47f..6e0ba341 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -841,7 +841,7 @@
         }
 
         @Override
-        public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
+        public boolean startAsCaller(Activity activity, Bundle options, int userId) {
             final Intent intent = getBaseIntentToSend();
             if (intent == null) {
                 return false;
@@ -860,7 +860,8 @@
             final boolean ignoreTargetSecurity = mSourceInfo != null
                     && mSourceInfo.getResolvedComponentName().getPackageName()
                     .equals(mChooserTarget.getComponentName().getPackageName());
-            return activity.startAsCallerImpl(intent, options, ignoreTargetSecurity, userId);
+            activity.startActivityAsCaller(intent, options, ignoreTargetSecurity, userId);
+            return true;
         }
 
         @Override
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 86731bc..398d087 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -107,7 +107,7 @@
                             || ChooserActivity.class.getName().equals(ri.activityInfo.name));
 
             try {
-                startActivityAsCaller(newIntent, null, null, false, targetUserId);
+                startActivityAsCaller(newIntent, null, false, targetUserId);
             } catch (RuntimeException e) {
                 int launchedFromUid = -1;
                 String launchedFromPackage = "?";
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index d6d4490..ceb06f5 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -43,7 +43,6 @@
 import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.IBinder;
 import android.os.PatternMatcher;
 import android.os.RemoteException;
 import android.os.StrictMode;
@@ -858,36 +857,6 @@
         }
     }
 
-    public boolean startAsCallerImpl(Intent intent, Bundle options, boolean ignoreTargetSecurity,
-            int userId) {
-        // Pass intent to delegate chooser activity with permission token.
-        // TODO: This should move to a trampoline Activity in the system when the ChooserActivity
-        // moves into systemui
-        try {
-            // TODO: Once this is a small springboard activity, it can move off the UI process
-            // and we can move the request method to ActivityManagerInternal.
-            IBinder permissionToken = ActivityManager.getService()
-                    .requestStartActivityPermissionToken(getActivityToken());
-            final Intent chooserIntent = new Intent();
-            final ComponentName delegateActivity = ComponentName.unflattenFromString(
-                    Resources.getSystem().getString(R.string.config_chooserActivity));
-            chooserIntent.setClassName(delegateActivity.getPackageName(),
-                    delegateActivity.getClassName());
-            chooserIntent.putExtra(ActivityManager.EXTRA_PERMISSION_TOKEN, permissionToken);
-
-            // TODO: These extras will change as chooser activity moves into systemui
-            chooserIntent.putExtra(Intent.EXTRA_INTENT, intent);
-            chooserIntent.putExtra(ActivityManager.EXTRA_OPTIONS, options);
-            chooserIntent.putExtra(ActivityManager.EXTRA_IGNORE_TARGET_SECURITY,
-                    ignoreTargetSecurity);
-            chooserIntent.putExtra(Intent.EXTRA_USER_ID, userId);
-            startActivity(chooserIntent);
-        } catch (RemoteException e) {
-            Log.e(TAG, e.toString());
-        }
-        return true;
-    }
-
     public void onActivityStarted(TargetInfo cti) {
         // Do nothing
     }
@@ -1212,8 +1181,9 @@
         }
 
         @Override
-        public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
-            return activity.startAsCallerImpl(mResolvedIntent, options, false, userId);
+        public boolean startAsCaller(Activity activity, Bundle options, int userId) {
+            activity.startActivityAsCaller(mResolvedIntent, options, false, userId);
+            return true;
         }
 
         @Override
@@ -1272,7 +1242,7 @@
          * @param userId userId to start as or {@link UserHandle#USER_NULL} for activity's caller
          * @return true if the start completed successfully
          */
-        boolean startAsCaller(ResolverActivity activity, Bundle options, int userId);
+        boolean startAsCaller(Activity activity, Bundle options, int userId);
 
         /**
          * Start the activity referenced by this target as a given user.
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index e91b67a..7def876 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -10896,7 +10896,7 @@
         return null;
     }
 
-    /**
+   /**
      * Distribute WiFi energy info and network traffic to apps.
      * @param info The energy information from the WiFi controller.
      */
@@ -11160,6 +11160,9 @@
         }
     }
 
+    private ModemActivityInfo mLastModemActivityInfo =
+            new ModemActivityInfo(0, 0, 0, new int[0], 0, 0);
+
     /**
      * Distribute Cell radio energy info and network traffic to apps.
      */
@@ -11180,6 +11183,22 @@
             }
         }
 
+        int rxTimeMs = 0;
+        int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+        int idleTimeMs = 0;
+        int sleepTimeMs = 0;
+        if (activityInfo != null) {
+            rxTimeMs = activityInfo.getRxTimeMillis() - mLastModemActivityInfo.getRxTimeMillis();
+            for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
+                txTimeMs[i] = activityInfo.getTxTimeMillis()[i]
+                        - mLastModemActivityInfo.getTxTimeMillis()[i];
+            }
+            idleTimeMs =
+                    activityInfo.getIdleTimeMillis() - mLastModemActivityInfo.getIdleTimeMillis();
+            sleepTimeMs =
+                    activityInfo.getSleepTimeMillis() - mLastModemActivityInfo.getSleepTimeMillis();
+        }
+
         synchronized (this) {
             if (!mOnBatteryInternal) {
                 if (delta != null) {
@@ -11191,11 +11210,11 @@
             if (activityInfo != null) {
                 mHasModemReporting = true;
                 mModemActivity.getIdleTimeCounter().addCountLocked(
-                    activityInfo.getIdleTimeMillis());
-                mModemActivity.getRxTimeCounter().addCountLocked(activityInfo.getRxTimeMillis());
+                    idleTimeMs);
+                mModemActivity.getRxTimeCounter().addCountLocked(rxTimeMs);
                 for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
                     mModemActivity.getTxTimeCounters()[lvl]
-                        .addCountLocked(activityInfo.getTxTimeMillis()[lvl]);
+                        .addCountLocked(txTimeMs[lvl]);
                 }
 
                 // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
@@ -11203,16 +11222,15 @@
                     PowerProfile.POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE) / 1000.0;
                 if (opVolt != 0) {
                     double energyUsed =
-                        activityInfo.getSleepTimeMillis() *
+                        sleepTimeMs *
                             mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_SLEEP)
-                            + activityInfo.getIdleTimeMillis() *
+                            + idleTimeMs *
                             mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE)
-                            + activityInfo.getRxTimeMillis() *
+                            + rxTimeMs *
                             mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX);
-                    int[] txCurrentMa = activityInfo.getTxTimeMillis();
-                    for (int i = 0; i < Math.min(txCurrentMa.length,
+                    for (int i = 0; i < Math.min(txTimeMs.length,
                         SignalStrength.NUM_SIGNAL_STRENGTH_BINS); i++) {
-                        energyUsed += txCurrentMa[i] * mPowerProfile.getAveragePower(
+                        energyUsed += txTimeMs[i] * mPowerProfile.getAveragePower(
                             PowerProfile.POWER_MODEM_CONTROLLER_TX, i);
                     }
 
@@ -11293,7 +11311,7 @@
                             ControllerActivityCounterImpl activityCounter =
                                     u.getOrCreateModemControllerActivityLocked();
                             if (totalRxPackets > 0 && entry.rxPackets > 0) {
-                                final long rxMs = (entry.rxPackets * activityInfo.getRxTimeMillis())
+                                final long rxMs = (entry.rxPackets * rxTimeMs)
                                         / totalRxPackets;
                                 activityCounter.getRxTimeCounter().addCountLocked(rxMs);
                             }
@@ -11301,7 +11319,7 @@
                             if (totalTxPackets > 0 && entry.txPackets > 0) {
                                 for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
                                     long txMs =
-                                            entry.txPackets * activityInfo.getTxTimeMillis()[lvl];
+                                            entry.txPackets * txTimeMs[lvl];
                                     txMs /= totalTxPackets;
                                     activityCounter.getTxTimeCounters()[lvl].addCountLocked(txMs);
                                 }
@@ -11322,6 +11340,10 @@
         }
     }
 
+    // Cache last value for comparison.
+    private BluetoothActivityEnergyInfo mLastBluetoothActivityEnergyInfo =
+            new BluetoothActivityEnergyInfo(0, 0, 0, 0, 0, 0);
+
     /**
      * Distribute Bluetooth energy info and network traffic to apps.
      * @param info The energy information from the bluetooth controller.
@@ -11338,14 +11360,17 @@
         mHasBluetoothReporting = true;
 
         final long elapsedRealtimeMs = mClocks.elapsedRealtime();
-        final long rxTimeMs = info.getControllerRxTimeMillis();
-        final long txTimeMs = info.getControllerTxTimeMillis();
-
+        final long rxTimeMs = info.getControllerRxTimeMillis() -
+                mLastBluetoothActivityEnergyInfo.getControllerRxTimeMillis();
+        final long txTimeMs = info.getControllerTxTimeMillis() -
+                mLastBluetoothActivityEnergyInfo.getControllerTxTimeMillis();
+        final long idleTimeMs = info.getControllerIdleTimeMillis() -
+                mLastBluetoothActivityEnergyInfo.getControllerIdleTimeMillis();
         if (DEBUG_ENERGY) {
             Slog.d(TAG, "------ BEGIN BLE power blaming ------");
             Slog.d(TAG, "  Tx Time:    " + txTimeMs + " ms");
             Slog.d(TAG, "  Rx Time:    " + rxTimeMs + " ms");
-            Slog.d(TAG, "  Idle Time:  " + info.getControllerIdleTimeMillis() + " ms");
+            Slog.d(TAG, "  Idle Time:  " + idleTimeMs + " ms");
         }
 
         long totalScanTimeMs = 0;
@@ -11424,9 +11449,25 @@
         long totalRxBytes = 0;
 
         final UidTraffic[] uidTraffic = info.getUidTraffic();
-        final int numUids = uidTraffic != null ? uidTraffic.length : 0;
-        for (int i = 0; i < numUids; i++) {
-            final UidTraffic traffic = uidTraffic[i];
+        final UidTraffic[] lastUidTraffic = mLastBluetoothActivityEnergyInfo.getUidTraffic();
+        final ArrayList<UidTraffic> deltaTraffic = new ArrayList<>();
+        int m = 0, n = 0;
+        for (; m < uidTraffic.length && n < lastUidTraffic.length; m++) {
+            final UidTraffic traffic = uidTraffic[m];
+            final UidTraffic lastTraffic = lastUidTraffic[n];
+            if (traffic.getUid() == lastTraffic.getUid()) {
+                deltaTraffic.add(new UidTraffic(traffic.getUid(),
+                        traffic.getRxBytes() - lastTraffic.getRxBytes(),
+                        traffic.getTxBytes() - lastTraffic.getTxBytes()));
+                n++;
+            }
+        }
+        for (; m < uidTraffic.length; m ++) {
+            deltaTraffic.add(uidTraffic[m]);
+        }
+
+        for (int i = 0, j = 0; i < deltaTraffic.size(); i++) {
+            final UidTraffic traffic = deltaTraffic.get(i);
 
             // Add to the global counters.
             mNetworkByteActivityCounters[NETWORK_BT_RX_DATA].addCountLocked(
@@ -11446,8 +11487,8 @@
 
         if ((totalTxBytes != 0 || totalRxBytes != 0) &&
                 (leftOverRxTimeMs != 0 || leftOverTxTimeMs != 0)) {
-            for (int i = 0; i < numUids; i++) {
-                final UidTraffic traffic = uidTraffic[i];
+            for (int i = 0; i < deltaTraffic.size(); i++) {
+                final UidTraffic traffic = deltaTraffic.get(i);
 
                 final Uid u = getUidStatsLocked(mapUid(traffic.getUid()));
                 final ControllerActivityCounterImpl counter =
@@ -11478,12 +11519,9 @@
             }
         }
 
-        mBluetoothActivity.getRxTimeCounter().addCountLocked(
-                info.getControllerRxTimeMillis());
-        mBluetoothActivity.getTxTimeCounters()[0].addCountLocked(
-                info.getControllerTxTimeMillis());
-        mBluetoothActivity.getIdleTimeCounter().addCountLocked(
-                info.getControllerIdleTimeMillis());
+        mBluetoothActivity.getRxTimeCounter().addCountLocked(rxTimeMs);
+        mBluetoothActivity.getTxTimeCounters()[0].addCountLocked(txTimeMs);
+        mBluetoothActivity.getIdleTimeCounter().addCountLocked(idleTimeMs);
 
         // POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V.
         final double opVolt = mPowerProfile.getAveragePower(
@@ -11491,8 +11529,10 @@
         if (opVolt != 0) {
             // We store the power drain as mAms.
             mBluetoothActivity.getPowerCounter().addCountLocked(
-                    (long) (info.getControllerEnergyUsed() / opVolt));
+                    (long) ((info.getControllerEnergyUsed() -
+                            mLastBluetoothActivityEnergyInfo.getControllerEnergyUsed() )/ opVolt));
         }
+        mLastBluetoothActivityEnergyInfo = info;
     }
 
     /**
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 49cbb54..115d0d5 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -24,6 +24,7 @@
 #include "core_jni_helpers.h"
 #include <nativehelper/ScopedStringChars.h>
 #include <nativehelper/ScopedUtfChars.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
 
 #include "SkBlurDrawLooper.h"
 #include "SkColorFilter.h"
@@ -515,11 +516,10 @@
             jint start, jint end, jint contextStart, jint contextEnd, jboolean isRtl, jint offset) {
         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         const Typeface* typeface = paint->getAndroidTypeface();
-        jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, nullptr);
-        jfloat result = doRunAdvance(paint, typeface, textArray + contextStart,
+        ScopedCharArrayRO textArray(env, text);
+        jfloat result = doRunAdvance(paint, typeface, textArray.get() + contextStart,
                 start - contextStart, end - start, contextEnd - contextStart, isRtl,
                 offset - contextStart);
-        env->ReleasePrimitiveArrayCritical(text, textArray, JNI_ABORT);
         return result;
     }
 
@@ -537,11 +537,10 @@
             jboolean isRtl, jfloat advance) {
         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         const Typeface* typeface = paint->getAndroidTypeface();
-        jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, nullptr);
-        jint result = doOffsetForAdvance(paint, typeface, textArray + contextStart,
+        ScopedCharArrayRO textArray(env, text);
+        jint result = doOffsetForAdvance(paint, typeface, textArray.get() + contextStart,
                 start - contextStart, end - start, contextEnd - contextStart, isRtl, advance);
         result += contextStart;
-        env->ReleasePrimitiveArrayCritical(text, textArray, JNI_ABORT);
         return result;
     }
 
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index 0d09b56..86cda44 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -519,8 +519,21 @@
             (void*)nativeDispose },
     { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V",
             (void*)nativeWriteToParcel },
+
     { "nativeGetName", "(J)Ljava/lang/String;",
             (void*)nativeGetName },
+    { "nativeGetBlob", "(JII)[B",
+            (void*)nativeGetBlob },
+    { "nativeGetString", "(JII)Ljava/lang/String;",
+            (void*)nativeGetString },
+    { "nativeCopyStringToBuffer", "(JIILandroid/database/CharArrayBuffer;)V",
+            (void*)nativeCopyStringToBuffer },
+    { "nativePutBlob", "(J[BII)Z",
+            (void*)nativePutBlob },
+    { "nativePutString", "(JLjava/lang/String;II)Z",
+            (void*)nativePutString },
+
+    // ------- @FastNative below here ----------------------
     { "nativeClear", "(J)V",
             (void*)nativeClear },
     { "nativeGetNumRows", "(J)I",
@@ -533,20 +546,11 @@
             (void*)nativeFreeLastRow },
     { "nativeGetType", "(JII)I",
             (void*)nativeGetType },
-    { "nativeGetBlob", "(JII)[B",
-            (void*)nativeGetBlob },
-    { "nativeGetString", "(JII)Ljava/lang/String;",
-            (void*)nativeGetString },
     { "nativeGetLong", "(JII)J",
             (void*)nativeGetLong },
     { "nativeGetDouble", "(JII)D",
             (void*)nativeGetDouble },
-    { "nativeCopyStringToBuffer", "(JIILandroid/database/CharArrayBuffer;)V",
-            (void*)nativeCopyStringToBuffer },
-    { "nativePutBlob", "(J[BII)Z",
-            (void*)nativePutBlob },
-    { "nativePutString", "(JLjava/lang/String;II)Z",
-            (void*)nativePutString },
+
     { "nativePutLong", "(JJII)Z",
             (void*)nativePutLong },
     { "nativePutDouble", "(JDII)Z",
diff --git a/core/jni/android_text_MeasuredParagraph.cpp b/core/jni/android_text_MeasuredParagraph.cpp
index f0e449d..dddddbb 100644
--- a/core/jni/android_text_MeasuredParagraph.cpp
+++ b/core/jni/android_text_MeasuredParagraph.cpp
@@ -115,6 +115,10 @@
     return toJLong(&releaseMeasuredParagraph);
 }
 
+static jint nGetMemoryUsage(jlong ptr) {
+    return static_cast<jint>(toMeasuredParagraph(ptr)->getMemoryUsage());
+}
+
 static const JNINativeMethod gMethods[] = {
     // MeasuredParagraphBuilder native functions.
     {"nInitBuilder", "()J", (void*) nInitBuilder},
@@ -126,6 +130,7 @@
     // MeasuredParagraph native functions.
     {"nGetWidth", "(JII)F", (void*) nGetWidth},  // Critical Natives
     {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc},  // Critical Natives
+    {"nGetMemoryUsage", "(J)I", (void*) nGetMemoryUsage},  // Critical Native
 };
 
 int register_android_text_MeasuredParagraph(JNIEnv* env) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 36082ca..a0ba3ad 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1959,12 +1959,6 @@
     <permission android:name="android.permission.START_ANY_ACTIVITY"
         android:protectionLevel="signature" />
 
-    <!-- Allows an application to start an activity as another app, provided that app has been
-         granted a permissionToken from the ActivityManagerService.
-         @hide -->
-    <permission android:name="android.permission.START_ACTIVITY_AS_CALLER"
-        android:protectionLevel="signature" />
-
     <!-- @deprecated The {@link android.app.ActivityManager#restartPackage}
         API is no longer supported. -->
     <permission android:name="android.permission.RESTART_PACKAGES"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 38f890a..a22ca87 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2283,10 +2283,7 @@
          Can be customized for other product types -->
     <string name="config_chooseTypeAndAccountActivity" translatable="false"
             >android/android.accounts.ChooseTypeAndAccountActivity</string>
-    <!-- Name of the activity that will handle requests to the system to choose an activity for
-         the purposes of resolving an intent. -->
-    <string name="config_chooserActivity" translatable="false"
-            >com.android.systemui/com.android.systemui.chooser.ChooserActivity</string>
+
     <!-- Component name of a custom ResolverActivity (Intent resolver) to be used instead of
          the default framework version. If left empty, then the framework version will be used.
          Example: com.google.android.myapp/.resolver.MyResolverActivity  -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e8ab0be..09d3121 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1071,7 +1071,6 @@
   <java-symbol type="string" name="owner_name" />
   <java-symbol type="string" name="config_chooseAccountActivity" />
   <java-symbol type="string" name="config_chooseTypeAndAccountActivity" />
-  <java-symbol type="string" name="config_chooserActivity" />
   <java-symbol type="string" name="config_customResolverActivity" />
   <java-symbol type="string" name="config_appsAuthorizedForSharedAccounts" />
   <java-symbol type="string" name="error_message_title" />
diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
index c0bc3a8..b18fa74 100644
--- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java
@@ -24,7 +24,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.os.Bundle;
-import android.os.IBinder;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.support.test.InstrumentationRegistry;
@@ -270,8 +269,8 @@
         }
 
         @Override
-        public void startActivityAsCaller(Intent intent, @Nullable Bundle options,
-                IBinder permissionToken, boolean ignoreTargetSecurity, int userId) {
+        public void startActivityAsCaller(Intent intent, @Nullable Bundle options, boolean
+                ignoreTargetSecurity, int userId) {
             mStartActivityIntent = intent;
             mUserIdActivityLaunchedIn = userId;
         }
@@ -294,4 +293,4 @@
             return mPm;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 6c8aaf0..8addffbb 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -369,7 +369,6 @@
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
         <permission name="android.permission.REAL_GET_TASKS"/>
         <permission name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE"/>
-        <permission name="android.permission.START_ACTIVITY_AS_CALLER"/>
         <permission name="android.permission.START_TASKS_FROM_RECENTS"/>
         <permission name="android.permission.STATUS_BAR"/>
         <permission name="android.permission.STOP_APP_SWITCHES"/>
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index ded427e..c429fd3 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -618,9 +618,9 @@
      * @return {@code KeyStore.NO_ERROR} on success, otherwise an error value corresponding to
      * a {@code KeymasterDefs.KM_ERROR_} value or {@code KeyStore} ResponseCode.
      */
-    public int addAuthToken(byte[] authToken) {
+    public int addAuthToken(byte[] authToken, int userId) {
         try {
-            return mBinder.addAuthToken(authToken);
+            return mBinder.addAuthToken(authToken, userId);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index f721ed3..419eb24 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -243,12 +243,7 @@
                 // Check that user authentication related parameters are acceptable. This method
                 // will throw an IllegalStateException if there are issues (e.g., secure lock screen
                 // not set up).
-                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
-                        spec.isUserAuthenticationRequired(),
-                        spec.getUserAuthenticationValidityDurationSeconds(),
-                        spec.isUserAuthenticationValidWhileOnBody(),
-                        spec.isInvalidatedByBiometricEnrollment(),
-                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), spec);
             } catch (IllegalStateException | IllegalArgumentException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -284,15 +279,7 @@
         args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
         args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings);
         args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
-        KeymasterUtils.addUserAuthArgs(args,
-                spec.isUserAuthenticationRequired(),
-                spec.getUserAuthenticationValidityDurationSeconds(),
-                spec.isUserAuthenticationValidWhileOnBody(),
-                spec.isInvalidatedByBiometricEnrollment(),
-                GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
-        if (spec.isTrustedUserPresenceRequired()) {
-            args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
-        }
+        KeymasterUtils.addUserAuthArgs(args, spec);
         KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                 args,
                 mKeymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index d1eb688..d68a33d 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -344,12 +344,7 @@
                 // Check that user authentication related parameters are acceptable. This method
                 // will throw an IllegalStateException if there are issues (e.g., secure lock screen
                 // not set up).
-                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
-                        mSpec.isUserAuthenticationRequired(),
-                        mSpec.getUserAuthenticationValidityDurationSeconds(),
-                        mSpec.isUserAuthenticationValidWhileOnBody(),
-                        mSpec.isInvalidatedByBiometricEnrollment(),
-                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), mSpec);
             } catch (IllegalArgumentException | IllegalStateException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -540,12 +535,7 @@
         args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings);
         args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
 
-        KeymasterUtils.addUserAuthArgs(args,
-                mSpec.isUserAuthenticationRequired(),
-                mSpec.getUserAuthenticationValidityDurationSeconds(),
-                mSpec.isUserAuthenticationValidWhileOnBody(),
-                mSpec.isInvalidatedByBiometricEnrollment(),
-                GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */);
+        KeymasterUtils.addUserAuthArgs(args, mSpec);
         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart());
         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
                 mSpec.getKeyValidityForOriginationEnd());
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 440e086..fc86ca0 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -497,12 +497,7 @@
                 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
                 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING,
                         KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
-                KeymasterUtils.addUserAuthArgs(importArgs,
-                        spec.isUserAuthenticationRequired(),
-                        spec.getUserAuthenticationValidityDurationSeconds(),
-                        spec.isUserAuthenticationValidWhileOnBody(),
-                        spec.isInvalidatedByBiometricEnrollment(),
-                        spec.getBoundToSpecificSecureUserId());
+                KeymasterUtils.addUserAuthArgs(importArgs, spec);
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
                         spec.getKeyValidityStart());
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
@@ -699,12 +694,7 @@
             int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
                     params.getEncryptionPaddings());
             args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
-            KeymasterUtils.addUserAuthArgs(args,
-                    params.isUserAuthenticationRequired(),
-                    params.getUserAuthenticationValidityDurationSeconds(),
-                    params.isUserAuthenticationValidWhileOnBody(),
-                    params.isInvalidatedByBiometricEnrollment(),
-                    params.getBoundToSpecificSecureUserId());
+            KeymasterUtils.addUserAuthArgs(args, params);
             KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                     args,
                     keymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index a896c72..0291b8a 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.app.KeyguardManager;
 import android.hardware.fingerprint.FingerprintManager;
+import android.security.GateKeeper;
 import android.security.KeyStore;
 import android.text.TextUtils;
 
@@ -232,7 +233,7 @@
  * key = (SecretKey) keyStore.getKey("key2", null);
  * }</pre>
  */
-public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
+public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs {
 
     private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
     private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
@@ -264,6 +265,7 @@
     private final boolean mUserAuthenticationValidWhileOnBody;
     private final boolean mInvalidatedByBiometricEnrollment;
     private final boolean mIsStrongBoxBacked;
+    private final boolean mUnlockedDeviceRequired;
 
     /**
      * @hide should be built with Builder
@@ -293,7 +295,8 @@
             boolean uniqueIdIncluded,
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
-            boolean isStrongBoxBacked) {
+            boolean isStrongBoxBacked,
+            boolean unlockedDeviceRequired) {
         if (TextUtils.isEmpty(keyStoreAlias)) {
             throw new IllegalArgumentException("keyStoreAlias must not be empty");
         }
@@ -341,6 +344,7 @@
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
         mIsStrongBoxBacked = isStrongBoxBacked;
+        mUnlockedDeviceRequired = unlockedDeviceRequired;
     }
 
     /**
@@ -646,6 +650,22 @@
     }
 
     /**
+     * Returns {@code true} if the key cannot be used unless the device screen is unlocked.
+     *
+     * @see Builder#SetUnlockedDeviceRequired(boolean)
+     */
+    public boolean isUnlockedDeviceRequired() {
+        return mUnlockedDeviceRequired;
+    }
+
+    /**
+     * @hide
+     */
+    public long getBoundToSpecificSecureUserId() {
+        return GateKeeper.INVALID_SECURE_USER_ID;
+    }
+
+    /**
      * Builder of {@link KeyGenParameterSpec} instances.
      */
     public final static class Builder {
@@ -675,6 +695,7 @@
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
         private boolean mIsStrongBoxBacked = false;
+        private boolean mUnlockedDeviceRequired = false;
 
         /**
          * Creates a new instance of the {@code Builder}.
@@ -1220,6 +1241,18 @@
         }
 
         /**
+         * Sets whether the keystore requires the screen to be unlocked before allowing decryption
+         * using this key. If this is set to {@code true}, any attempt to decrypt using this key
+         * while the screen is locked will fail. A locked device requires a PIN, password,
+         * fingerprint, or other trusted factor to access.
+         */
+        @NonNull
+        public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) {
+            mUnlockedDeviceRequired = unlockedDeviceRequired;
+            return this;
+        }
+
+        /**
          * Builds an instance of {@code KeyGenParameterSpec}.
          */
         @NonNull
@@ -1249,7 +1282,8 @@
                     mUniqueIdIncluded,
                     mUserAuthenticationValidWhileOnBody,
                     mInvalidatedByBiometricEnrollment,
-                    mIsStrongBoxBacked);
+                    mIsStrongBoxBacked,
+                    mUnlockedDeviceRequired);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index dbacb9c..a5b85ce 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -212,7 +212,7 @@
  * ...
  * }</pre>
  */
-public final class KeyProtection implements ProtectionParameter {
+public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
     private final Date mKeyValidityStart;
     private final Date mKeyValidityForOriginationEnd;
     private final Date mKeyValidityForConsumptionEnd;
@@ -228,6 +228,8 @@
     private final boolean mInvalidatedByBiometricEnrollment;
     private final long mBoundToSecureUserId;
     private final boolean mCriticalToDeviceEncryption;
+    private final boolean mTrustedUserPresenceRequired;
+    private final boolean mUnlockedDeviceRequired;
 
     private KeyProtection(
             Date keyValidityStart,
@@ -241,10 +243,12 @@
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
+            boolean trustedUserPresenceRequired,
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
             long boundToSecureUserId,
-            boolean criticalToDeviceEncryption) {
+            boolean criticalToDeviceEncryption,
+            boolean unlockedDeviceRequired) {
         mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart);
         mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd);
         mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
@@ -262,6 +266,8 @@
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
         mBoundToSecureUserId = boundToSecureUserId;
         mCriticalToDeviceEncryption = criticalToDeviceEncryption;
+        mTrustedUserPresenceRequired = trustedUserPresenceRequired;
+        mUnlockedDeviceRequired = unlockedDeviceRequired;
     }
 
     /**
@@ -414,6 +420,14 @@
     }
 
     /**
+     * Returns {@code true} if the key is authorized to be used only if a test of user presence has
+     * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
+     */
+    public boolean isTrustedUserPresenceRequired() {
+        return mTrustedUserPresenceRequired;
+    }
+
+    /**
      * Returns {@code true} if the key will be de-authorized when the device is removed from the
      * user's body.  This option has no effect on keys that don't have an authentication validity
      * duration, and has no effect if the device lacks an on-body sensor.
@@ -471,6 +485,15 @@
     }
 
     /**
+     * Returns {@code true} if the key cannot be used unless the device screen is unlocked.
+     *
+     * @see Builder#SetRequireDeviceUnlocked(boolean)
+     */
+    public boolean isUnlockedDeviceRequired() {
+        return mUnlockedDeviceRequired;
+    }
+
+    /**
      * Builder of {@link KeyProtection} instances.
      */
     public final static class Builder {
@@ -488,6 +511,9 @@
         private int mUserAuthenticationValidityDurationSeconds = -1;
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
+        private boolean mTrustedUserPresenceRequired = false;
+        private boolean mUnlockedDeviceRequired = false;
+
         private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
         private boolean mCriticalToDeviceEncryption = false;
 
@@ -764,6 +790,16 @@
         }
 
         /**
+         * Sets whether a test of user presence is required to be performed between the
+         * {@code Signature.initSign()} and {@code Signature.sign()} method calls.
+         */
+        @NonNull
+        public Builder setTrustedUserPresenceRequired(boolean required) {
+            mTrustedUserPresenceRequired = required;
+            return this;
+        }
+
+        /**
          * Sets whether the key will remain authorized only until the device is removed from the
          * user's body up to the limit of the authentication validity period (see
          * {@link #setUserAuthenticationValidityDurationSeconds} and
@@ -845,6 +881,18 @@
         }
 
         /**
+         * Sets whether the keystore requires the screen to be unlocked before allowing decryption
+         * using this key. If this is set to {@code true}, any attempt to decrypt using this key
+         * while the screen is locked will fail. A locked device requires a PIN, password,
+         * fingerprint, or other trusted factor to access.
+         */
+        @NonNull
+        public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) {
+            mUnlockedDeviceRequired = unlockedDeviceRequired;
+            return this;
+        }
+
+        /**
          * Builds an instance of {@link KeyProtection}.
          *
          * @throws IllegalArgumentException if a required field is missing
@@ -863,10 +911,12 @@
                     mRandomizedEncryptionRequired,
                     mUserAuthenticationRequired,
                     mUserAuthenticationValidityDurationSeconds,
+                    mTrustedUserPresenceRequired,
                     mUserAuthenticationValidWhileOnBody,
                     mInvalidatedByBiometricEnrollment,
                     mBoundToSecureUserId,
-                    mCriticalToDeviceEncryption);
+                    mCriticalToDeviceEncryption,
+                    mUnlockedDeviceRequired);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index 34c8d1f..eb6a2a2 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -98,17 +98,23 @@
      *         require user authentication.
      */
     public static void addUserAuthArgs(KeymasterArguments args,
-            boolean userAuthenticationRequired,
-            int userAuthenticationValidityDurationSeconds,
-            boolean userAuthenticationValidWhileOnBody,
-            boolean invalidatedByBiometricEnrollment,
-            long boundToSpecificSecureUserId) {
-        if (!userAuthenticationRequired) {
+            UserAuthArgs spec) {
+        args.addUnsignedInt(KeymasterDefs.KM_TAG_USER_ID, 0);
+
+        if (spec.isTrustedUserPresenceRequired()) {
+            args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
+        }
+
+        if (spec.isUnlockedDeviceRequired()) {
+            args.addBoolean(KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED);
+        }
+
+        if (!spec.isUserAuthenticationRequired()) {
             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
             return;
         }
 
-        if (userAuthenticationValidityDurationSeconds == -1) {
+        if (spec.getUserAuthenticationValidityDurationSeconds() == -1) {
             // Every use of this key needs to be authorized by the user. This currently means
             // fingerprint-only auth.
             FingerprintManager fingerprintManager =
@@ -124,9 +130,9 @@
             }
 
             long sid;
-            if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) {
-                sid = boundToSpecificSecureUserId;
-            } else if (invalidatedByBiometricEnrollment) {
+            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+                sid = spec.getBoundToSpecificSecureUserId();
+            } else if (spec.isInvalidatedByBiometricEnrollment()) {
                 // The fingerprint-only SID will change on fingerprint enrollment or removal of all,
                 // enrolled fingerprints, invalidating the key.
                 sid = fingerprintOnlySid;
@@ -139,14 +145,14 @@
             args.addUnsignedLong(
                     KeymasterDefs.KM_TAG_USER_SECURE_ID, KeymasterArguments.toUint64(sid));
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_FINGERPRINT);
-            if (userAuthenticationValidWhileOnBody) {
+            if (spec.isUserAuthenticationValidWhileOnBody()) {
                 throw new ProviderException("Key validity extension while device is on-body is not "
                         + "supported for keys requiring fingerprint authentication");
             }
         } else {
             long sid;
-            if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) {
-                sid = boundToSpecificSecureUserId;
+            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+                sid = spec.getBoundToSpecificSecureUserId();
             } else {
                 // The key is authorized for use for the specified amount of time after the user has
                 // authenticated. Whatever unlocks the secure lock screen should authorize this key.
@@ -157,8 +163,8 @@
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
                     KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_FINGERPRINT);
             args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
-                    userAuthenticationValidityDurationSeconds);
-            if (userAuthenticationValidWhileOnBody) {
+                    spec.getUserAuthenticationValidityDurationSeconds());
+            if (spec.isUserAuthenticationValidWhileOnBody()) {
                 args.addBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
             }
         }
diff --git a/keystore/java/android/security/keystore/UserAuthArgs.java b/keystore/java/android/security/keystore/UserAuthArgs.java
new file mode 100644
index 0000000..6fb3486
--- /dev/null
+++ b/keystore/java/android/security/keystore/UserAuthArgs.java
@@ -0,0 +1,37 @@
+/*
+ * 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.security.keystore;
+
+/**
+ * @hide
+ *
+ * This is an interface to encapsulate the user authentication arguments that
+ * are passed to KeymasterUtils.addUserAuthArgs. Classes that represent
+ * authorization characteristics for new or imported keys can implement this
+ * interface to be passed to that method.
+ */
+public interface UserAuthArgs {
+
+    boolean isUserAuthenticationRequired();
+    int getUserAuthenticationValidityDurationSeconds();
+    boolean isUserAuthenticationValidWhileOnBody();
+    boolean isInvalidatedByBiometricEnrollment();
+    boolean isTrustedUserPresenceRequired();
+    boolean isUnlockedDeviceRequired();
+    long getBoundToSpecificSecureUserId();
+
+}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 2ac4063..7533701 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -400,6 +400,18 @@
     public static final int ADJUST_TOGGLE_MUTE = 101;
 
     /** @hide */
+    @IntDef(flag = false, prefix = "ADJUST", value = {
+            ADJUST_RAISE,
+            ADJUST_LOWER,
+            ADJUST_SAME,
+            ADJUST_MUTE,
+            ADJUST_UNMUTE,
+            ADJUST_TOGGLE_MUTE }
+            )
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface VolumeAdjustement {}
+
+    /** @hide */
     public static final String adjustToString(int adj) {
         switch (adj) {
             case ADJUST_RAISE: return "ADJUST_RAISE";
@@ -2989,7 +3001,7 @@
         final IAudioService service = getService();
         try {
             String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(),
-                    policy.hasFocusListener(), policy.isFocusPolicy());
+                    policy.hasFocusListener(), policy.isFocusPolicy(), policy.isVolumeController());
             if (regId == null) {
                 return ERROR;
             } else {
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 6c65223..88d0a60 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -166,7 +166,8 @@
     boolean isHdmiSystemAudioSupported();
 
     String registerAudioPolicy(in AudioPolicyConfig policyConfig,
-            in IAudioPolicyCallback pcb, boolean hasFocusListener, boolean isFocusPolicy);
+            in IAudioPolicyCallback pcb, boolean hasFocusListener, boolean isFocusPolicy,
+            boolean isVolumeController);
 
     oneway void unregisterAudioPolicyAsync(in IAudioPolicyCallback pcb);
 
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index d36df84..d84eedf 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -96,22 +96,13 @@
  *         {@link #close()} is called, it is in the <em>End</em> state. Between these
  *         two states is the life cycle of the MediaPlayer2 object.
  *         <ul>
- *         <li>There is a subtle but important difference between a newly constructed
- *         MediaPlayer2 object and the MediaPlayer2 object after {@link #reset()}
- *         is called. It is a programming error to invoke methods such
+ *         <li> It is a programming error to invoke methods such
  *         as {@link #getCurrentPosition()},
  *         {@link #getDuration()}, {@link #getVideoHeight()},
  *         {@link #getVideoWidth()}, {@link #setAudioAttributes(AudioAttributes)},
  *         {@link #setVolume(float, float)}, {@link #pause()}, {@link #play()},
  *         {@link #seekTo(long, int)} or
- *         {@link #prepareAsync()} in the <em>Idle</em> state for both cases. If any of these
- *         methods is called right after a MediaPlayer2 object is constructed,
- *         the user supplied callback method OnErrorListener.onError() won't be
- *         called by the internal player engine and the object state remains
- *         unchanged; but if these methods are called right after {@link #reset()},
- *         the user supplied callback method OnErrorListener.onError() will be
- *         invoked by the internal player engine and the object will be
- *         transfered to the <em>Error</em> state. </li>
+ *         {@link #prepareAsync()} in the <em>Idle</em> state.
  *         <li>It is also recommended that once
  *         a MediaPlayer2 object is no longer being used, call {@link #close()} immediately
  *         so that resources used by the internal player engine associated with the
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 86a285c..222c66e 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -1960,6 +1960,13 @@
             mTimeProvider = null;
         }
 
+        synchronized (mEventCbLock) {
+            mEventCallbackRecords.clear();
+        }
+        synchronized (mDrmEventCbLock) {
+            mDrmEventCallbackRecords.clear();
+        }
+
         stayAwake(false);
         _reset();
         // make sure none of the listeners get called anymore
@@ -3049,8 +3056,7 @@
         stayAwake(false);
         updateSurfaceScreenOn();
         synchronized (mEventCbLock) {
-            mEventCb = null;
-            mEventExec = null;
+            mEventCallbackRecords.clear();
         }
         if (mTimeProvider != null) {
             mTimeProvider.close();
@@ -3061,8 +3067,7 @@
         // Modular DRM clean up
         mOnDrmConfigHelper = null;
         synchronized (mDrmEventCbLock) {
-            mDrmEventCb = null;
-            mDrmEventExec = null;
+            mDrmEventCallbackRecords.clear();
         }
         resetDrmState();
 
@@ -3118,18 +3123,8 @@
                 Log.w(TAG, "mediaplayer2 went away with unhandled events");
                 return;
             }
-            final Executor eventExec;
-            final EventCallback eventCb;
-            synchronized (mEventCbLock) {
-                eventExec = mEventExec;
-                eventCb = mEventCb;
-            }
-            final Executor drmEventExec;
-            final DrmEventCallback drmEventCb;
-            synchronized (mDrmEventCbLock) {
-                drmEventExec = mDrmEventExec;
-                drmEventCb = mDrmEventCb;
-            }
+            final int what = msg.arg1;
+            final int extra = msg.arg2;
             switch(msg.what) {
             case MEDIA_PREPARED:
                 try {
@@ -3143,33 +3138,36 @@
                     sendMessage(msg2);
                 }
 
-                if (eventCb != null && eventExec != null) {
-                    eventExec.execute(() -> eventCb.onInfo(
-                            mMediaPlayer, 0, MEDIA_INFO_PREPARED, 0));
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onInfo(
+                                mMediaPlayer, 0, MEDIA_INFO_PREPARED, 0));
+                    }
                 }
                 return;
 
             case MEDIA_DRM_INFO:
-                Log.v(TAG, "MEDIA_DRM_INFO " + mDrmEventCb);
-
                 if (msg.obj == null) {
                     Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL");
                 } else if (msg.obj instanceof Parcel) {
-                    if (drmEventExec != null && drmEventCb != null) {
-                        // The parcel was parsed already in postEventFromNative
-                        final DrmInfoImpl drmInfo;
+                    // The parcel was parsed already in postEventFromNative
+                    final DrmInfoImpl drmInfo;
 
-                        synchronized (mDrmLock) {
-                            if (mDrmInfoImpl != null) {
-                                drmInfo = mDrmInfoImpl.makeCopy();
-                            } else {
-                                drmInfo = null;
-                            }
+                    synchronized (mDrmLock) {
+                        if (mDrmInfoImpl != null) {
+                            drmInfo = mDrmInfoImpl.makeCopy();
+                        } else {
+                            drmInfo = null;
                         }
+                    }
 
-                        // notifying the client outside the lock
-                        if (drmInfo != null) {
-                            drmEventExec.execute(() -> drmEventCb.onDrmInfo(mMediaPlayer, drmInfo));
+                    // notifying the client outside the lock
+                    if (drmInfo != null) {
+                        synchronized (mEventCbLock) {
+                            for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+                                cb.first.execute(() -> cb.second.onDrmInfo(
+                                        mMediaPlayer, drmInfo));
+                            }
                         }
                     }
                 } else {
@@ -3178,9 +3176,11 @@
                 return;
 
             case MEDIA_PLAYBACK_COMPLETE:
-                if (eventCb != null && eventExec != null) {
-                    eventExec.execute(() -> eventCb.onInfo(
-                            mMediaPlayer, 0, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onInfo(
+                                mMediaPlayer, 0, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+                    }
                 }
                 stayAwake(false);
                 return;
@@ -3205,16 +3205,21 @@
                 break;
 
             case MEDIA_BUFFERING_UPDATE:
-                if (eventCb != null && eventExec != null) {
-                    final int percent = msg.arg1;
-                    eventExec.execute(() -> eventCb.onBufferingUpdate(mMediaPlayer, 0, percent));
+                final int percent = msg.arg1;
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onBufferingUpdate(
+                                mMediaPlayer, 0, percent));
+                    }
                 }
                 return;
 
             case MEDIA_SEEK_COMPLETE:
-                if (eventCb != null && eventExec != null) {
-                    eventExec.execute(() -> eventCb.onInfo(
-                            mMediaPlayer, 0, MEDIA_INFO_COMPLETE_CALL_SEEK, 0));
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onInfo(
+                                mMediaPlayer, 0, MEDIA_INFO_COMPLETE_CALL_SEEK, 0));
+                    }
                 }
                 // fall through
 
@@ -3228,61 +3233,68 @@
                 return;
 
             case MEDIA_SET_VIDEO_SIZE:
-                if (eventCb != null && eventExec != null) {
-                    final int width = msg.arg1;
-                    final int height = msg.arg2;
-                    eventExec.execute(() -> eventCb.onVideoSizeChanged(
-                            mMediaPlayer, 0, width, height));
+                final int width = msg.arg1;
+                final int height = msg.arg2;
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onVideoSizeChanged(
+                                mMediaPlayer, 0, width, height));
+                    }
                 }
                 return;
 
             case MEDIA_ERROR:
                 Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
-                if (eventCb != null && eventExec != null) {
-                    final int what = msg.arg1;
-                    final int extra = msg.arg2;
-                    eventExec.execute(() -> eventCb.onError(mMediaPlayer, 0, what, extra));
-                    eventExec.execute(() -> eventCb.onInfo(
-                            mMediaPlayer, 0, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onError(
+                                mMediaPlayer, 0, what, extra));
+                        cb.first.execute(() -> cb.second.onInfo(
+                                mMediaPlayer, 0, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+                    }
                 }
                 stayAwake(false);
                 return;
 
             case MEDIA_INFO:
                 switch (msg.arg1) {
-                case MEDIA_INFO_VIDEO_TRACK_LAGGING:
-                    Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
-                    break;
-                case MEDIA_INFO_METADATA_UPDATE:
-                    try {
-                        scanInternalSubtitleTracks();
-                    } catch (RuntimeException e) {
-                        Message msg2 = obtainMessage(
-                                MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
-                        sendMessage(msg2);
-                    }
-                    // fall through
+                    case MEDIA_INFO_VIDEO_TRACK_LAGGING:
+                        Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
+                        break;
 
-                case MEDIA_INFO_EXTERNAL_METADATA_UPDATE:
-                    msg.arg1 = MEDIA_INFO_METADATA_UPDATE;
-                    // update default track selection
-                    if (mSubtitleController != null) {
-                        mSubtitleController.selectDefaultTrack();
-                    }
-                    break;
-                case MEDIA_INFO_BUFFERING_START:
-                case MEDIA_INFO_BUFFERING_END:
-                    TimeProvider timeProvider = mTimeProvider;
-                    if (timeProvider != null) {
-                        timeProvider.onBuffering(msg.arg1 == MEDIA_INFO_BUFFERING_START);
-                    }
-                    break;
+                    case MEDIA_INFO_METADATA_UPDATE:
+                        try {
+                            scanInternalSubtitleTracks();
+                        } catch (RuntimeException e) {
+                            Message msg2 = obtainMessage(
+                                    MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED,
+                                    null);
+                            sendMessage(msg2);
+                        }
+                        // fall through
+
+                    case MEDIA_INFO_EXTERNAL_METADATA_UPDATE:
+                        msg.arg1 = MEDIA_INFO_METADATA_UPDATE;
+                        // update default track selection
+                        if (mSubtitleController != null) {
+                            mSubtitleController.selectDefaultTrack();
+                        }
+                        break;
+
+                    case MEDIA_INFO_BUFFERING_START:
+                    case MEDIA_INFO_BUFFERING_END:
+                        TimeProvider timeProvider = mTimeProvider;
+                        if (timeProvider != null) {
+                            timeProvider.onBuffering(msg.arg1 == MEDIA_INFO_BUFFERING_START);
+                        }
+                        break;
                 }
 
-                if (eventCb != null && eventExec != null) {
-                    final int what = msg.arg1;
-                    final int extra = msg.arg2;
-                    eventExec.execute(() -> eventCb.onInfo(mMediaPlayer, 0, what, extra));
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onInfo(
+                                mMediaPlayer, 0, what, extra));
+                    }
                 }
                 // No real default action so far.
                 return;
@@ -3295,17 +3307,18 @@
                 return;
 
             case MEDIA_TIMED_TEXT:
-                if (eventCb == null || eventExec == null) {
-                    return;
-                }
-                if (msg.obj == null) {
-                    eventExec.execute(() -> eventCb.onTimedText(mMediaPlayer, 0, null));
+                final TimedText text;
+                if (msg.obj instanceof Parcel) {
+                    Parcel parcel = (Parcel)msg.obj;
+                    text = new TimedText(parcel);
+                    parcel.recycle();
                 } else {
-                    if (msg.obj instanceof Parcel) {
-                        Parcel parcel = (Parcel)msg.obj;
-                        TimedText text = new TimedText(parcel);
-                        parcel.recycle();
-                        eventExec.execute(() -> eventCb.onTimedText(mMediaPlayer, 0, text));
+                    text = null;
+                }
+
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onTimedText(mMediaPlayer, 0, text));
                     }
                 }
                 return;
@@ -3324,15 +3337,20 @@
                 return;
 
             case MEDIA_META_DATA:
-                if (eventCb == null || eventExec == null) {
-                    return;
-                }
+                final TimedMetaData data;
                 if (msg.obj instanceof Parcel) {
                     Parcel parcel = (Parcel) msg.obj;
-                    TimedMetaData data = TimedMetaData.createTimedMetaDataFromParcel(parcel);
+                    data = TimedMetaData.createTimedMetaDataFromParcel(parcel);
                     parcel.recycle();
-                    eventExec.execute(() -> eventCb.onTimedMetaDataAvailable(
-                            mMediaPlayer, 0, data));
+                } else {
+                    data = null;
+                }
+
+                synchronized (mEventCbLock) {
+                    for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onTimedMetaDataAvailable(
+                                mMediaPlayer, 0, data));
+                    }
                 }
                 return;
 
@@ -3420,9 +3438,9 @@
         }
     }
 
-    private Executor mEventExec;
-    private EventCallback mEventCb;
     private final Object mEventCbLock = new Object();
+    private ArrayList<Pair<Executor, EventCallback> > mEventCallbackRecords
+        = new ArrayList<Pair<Executor, EventCallback> >();
 
     /**
      * Register a callback to be invoked when the media source is ready
@@ -3441,9 +3459,7 @@
             throw new IllegalArgumentException("Illegal null Executor for the EventCallback");
         }
         synchronized (mEventCbLock) {
-            // TODO: support multiple callbacks.
-            mEventExec = executor;
-            mEventCb = eventCallback;
+            mEventCallbackRecords.add(new Pair(executor, eventCallback));
         }
     }
 
@@ -3455,9 +3471,10 @@
     @Override
     public void unregisterEventCallback(EventCallback callback) {
         synchronized (mEventCbLock) {
-            if (callback == mEventCb) {
-                mEventExec = null;
-                mEventCb = null;
+            for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+                if (cb.second == callback) {
+                    mEventCallbackRecords.remove(cb);
+                }
             }
         }
     }
@@ -3497,9 +3514,9 @@
 
     private OnDrmConfigHelper mOnDrmConfigHelper;
 
-    private Executor mDrmEventExec;
-    private DrmEventCallback mDrmEventCb;
     private final Object mDrmEventCbLock = new Object();
+    private ArrayList<Pair<Executor, DrmEventCallback> > mDrmEventCallbackRecords
+        = new ArrayList<Pair<Executor, DrmEventCallback> >();
 
     /**
      * Register a callback to be invoked when the media source is ready
@@ -3518,9 +3535,7 @@
             throw new IllegalArgumentException("Illegal null Executor for the EventCallback");
         }
         synchronized (mDrmEventCbLock) {
-            // TODO: support multiple callbacks.
-            mDrmEventExec = executor;
-            mDrmEventCb = eventCallback;
+            mDrmEventCallbackRecords.add(new Pair(executor, eventCallback));
         }
     }
 
@@ -3532,9 +3547,11 @@
     @Override
     public void unregisterDrmEventCallback(DrmEventCallback callback) {
         synchronized (mDrmEventCbLock) {
-            if (callback == mDrmEventCb) {
-                mDrmEventExec = null;
-                mDrmEventCb = null;
+            for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+                if (cb.second == callback) {
+                    mDrmEventCallbackRecords.remove(cb);
+                    break;
+                }
             }
         }
     }
@@ -3733,15 +3750,11 @@
 
         // if finished successfully without provisioning, call the callback outside the lock
         if (allDoneWithoutProvisioning) {
-            final Executor drmEventExec;
-            final DrmEventCallback drmEventCb;
             synchronized (mDrmEventCbLock) {
-                drmEventExec = mDrmEventExec;
-                drmEventCb = mDrmEventCb;
-            }
-            if (drmEventExec != null && drmEventCb != null) {
-                drmEventExec.execute(() -> drmEventCb.onDrmPrepared(
-                    this, PREPARE_DRM_STATUS_SUCCESS));
+                for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+                    cb.first.execute(() -> cb.second.onDrmPrepared(
+                            this, PREPARE_DRM_STATUS_SUCCESS));
+                }
             }
         }
 
@@ -4324,14 +4337,12 @@
 
             boolean succeeded = false;
 
-            final Executor drmEventExec;
-            final DrmEventCallback drmEventCb;
+            boolean hasCallback = false;
             synchronized (mDrmEventCbLock) {
-                drmEventExec = mDrmEventExec;
-                drmEventCb = mDrmEventCb;
+                hasCallback = !mDrmEventCallbackRecords.isEmpty();
             }
             // non-blocking mode needs the lock
-            if (drmEventExec != null && drmEventCb != null) {
+            if (hasCallback) {
 
                 synchronized (drmLock) {
                     // continuing with prepareDrm
@@ -4349,7 +4360,11 @@
                 } // synchronized
 
                 // calling the callback outside the lock
-                drmEventExec.execute(() -> drmEventCb.onDrmPrepared(mediaPlayer, status));
+                synchronized (mDrmEventCbLock) {
+                    for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
+                        cb.first.execute(() -> cb.second.onDrmPrepared(mediaPlayer, status));
+                    }
+                }
             } else {   // blocking mode already has the lock
 
                 // continuing with prepareDrm
@@ -4397,13 +4412,11 @@
         int result;
 
         // non-blocking: this is not the final result
-        final Executor drmEventExec;
-        final DrmEventCallback drmEventCb;
+        boolean hasCallback = false;
         synchronized (mDrmEventCbLock) {
-            drmEventExec = mDrmEventExec;
-            drmEventCb = mDrmEventCb;
+            hasCallback = !mDrmEventCallbackRecords.isEmpty();
         }
-        if (drmEventCb != null && drmEventExec != null) {
+        if (hasCallback) {
             result = PREPARE_DRM_STATUS_SUCCESS;
         } else {
             // if blocking mode, wait till provisioning is done
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 7e88c27..d0bba6d 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -89,6 +89,8 @@
 
     private AudioPolicyFocusListener mFocusListener;
 
+    private final AudioPolicyVolumeCallback mVolCb;
+
     private Context mContext;
 
     private AudioPolicyConfig mConfig;
@@ -99,12 +101,15 @@
     public boolean hasFocusListener() { return mFocusListener != null; }
     /** @hide */
     public boolean isFocusPolicy() { return mIsFocusPolicy; }
+    /** @hide */
+    public boolean isVolumeController() { return mVolCb != null; }
 
     /**
      * The parameter is guaranteed non-null through the Builder
      */
     private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper,
-            AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy) {
+            AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy,
+            AudioPolicyVolumeCallback vc) {
         mConfig = config;
         mStatus = POLICY_STATUS_UNREGISTERED;
         mContext = context;
@@ -120,6 +125,7 @@
         mFocusListener = fl;
         mStatusListener = sl;
         mIsFocusPolicy = isFocusPolicy;
+        mVolCb = vc;
     }
 
     /**
@@ -134,6 +140,7 @@
         private AudioPolicyFocusListener mFocusListener;
         private AudioPolicyStatusListener mStatusListener;
         private boolean mIsFocusPolicy = false;
+        private AudioPolicyVolumeCallback mVolCb;
 
         /**
          * Constructs a new Builder with no audio mixes.
@@ -208,6 +215,22 @@
             mStatusListener = l;
         }
 
+        @SystemApi
+        /**
+         * Sets the callback to receive all volume key-related events.
+         * The callback will only be called if the device is configured to handle volume events
+         * in the PhoneWindowManager (see config_handleVolumeKeysInWindowManager)
+         * @param vc
+         * @return the same Builder instance.
+         */
+        public Builder setAudioPolicyVolumeCallback(@NonNull AudioPolicyVolumeCallback vc) {
+            if (vc == null) {
+                throw new IllegalArgumentException("Invalid null volume callback");
+            }
+            mVolCb = vc;
+            return this;
+        }
+
         /**
          * Combines all of the attributes that have been set on this {@code Builder} and returns a
          * new {@link AudioPolicy} object.
@@ -229,7 +252,7 @@
                         + "an AudioPolicyFocusListener");
             }
             return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper,
-                    mFocusListener, mStatusListener, mIsFocusPolicy);
+                    mFocusListener, mStatusListener, mIsFocusPolicy, mVolCb);
         }
     }
 
@@ -455,6 +478,23 @@
         public void onAudioFocusAbandon(AudioFocusInfo afi) {}
     }
 
+    @SystemApi
+    /**
+     * Callback class to receive volume change-related events.
+     * See {@link #Builder.setAudioPolicyVolumeCallback(AudioPolicyCallback)} to configure the
+     * {@link AudioPolicy} to receive those events.
+     *
+     */
+    public static abstract class AudioPolicyVolumeCallback {
+        /** @hide */
+        public AudioPolicyVolumeCallback() {}
+        /**
+         * Called when volume key-related changes are triggered, on the key down event.
+         * @param adjustement the type of volume adjustment for the key.
+         */
+        public void onVolumeAdjustment(@AudioManager.VolumeAdjustement int adjustement) {}
+    }
+
     private void onPolicyStatusChange() {
         AudioPolicyStatusListener l;
         synchronized (mLock) {
@@ -517,6 +557,13 @@
                 }
             }
         }
+
+        public void notifyVolumeAdjust(int adjustment) {
+            sendMsg(MSG_VOL_ADJUST, null /* ignored */, adjustment);
+            if (DEBUG) {
+                Log.v(TAG, "notifyVolumeAdjust: " + adjustment);
+            }
+        }
     };
 
     //==================================================
@@ -528,6 +575,7 @@
     private final static int MSG_MIX_STATE_UPDATE = 3;
     private final static int MSG_FOCUS_REQUEST = 4;
     private final static int MSG_FOCUS_ABANDON = 5;
+    private final static int MSG_VOL_ADJUST = 6;
 
     private class EventHandler extends Handler {
         public EventHandler(AudioPolicy ap, Looper looper) {
@@ -571,6 +619,12 @@
                         Log.e(TAG, "Invalid null focus listener for focus abandon event");
                     }
                     break;
+                case MSG_VOL_ADJUST:
+                    if (mVolCb != null) {
+                        mVolCb.onVolumeAdjustment(msg.arg1);
+                    } else { // should never be null, but don't crash
+                        Log.e(TAG, "Invalid null volume event");
+                    }
                 default:
                     Log.e(TAG, "Unknown event " + msg.what);
             }
diff --git a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
index 86abbb4..107e7cd 100644
--- a/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
+++ b/media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl
@@ -31,4 +31,7 @@
 
     // callback for mix activity status update
     void notifyMixStateUpdate(in String regId, int state);
+
+    // callback for volume events
+    void notifyVolumeAdjust(int adjustment);
 }
diff --git a/media/java/android/media/update/MediaControlView2Provider.java b/media/java/android/media/update/MediaControlView2Provider.java
index 6b38c92..4464f8f 100644
--- a/media/java/android/media/update/MediaControlView2Provider.java
+++ b/media/java/android/media/update/MediaControlView2Provider.java
@@ -37,11 +37,8 @@
 public interface MediaControlView2Provider extends ViewProvider {
     void setController_impl(MediaController controller);
     void show_impl();
-    void show_impl(int timeout);
+    void show_impl(long timeout);
     boolean isShowing_impl();
     void hide_impl();
-    void showSubtitle_impl();
-    void hideSubtitle_impl();
-    void setPrevNextListeners_impl(View.OnClickListener next, View.OnClickListener prev);
-    void setButtonVisibility_impl(int button, boolean visible);
+    void setButtonVisibility_impl(int button, int visibility);
 }
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 42ebf6a..ff854c5 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -68,17 +68,26 @@
     }
 }
 
-static MediaMetadataRetriever* getRetriever(JNIEnv* env, jobject thiz)
+static sp<MediaMetadataRetriever> getRetriever(JNIEnv* env, jobject thiz)
 {
     // No lock is needed, since it is called internally by other methods that are protected
     MediaMetadataRetriever* retriever = (MediaMetadataRetriever*) env->GetLongField(thiz, fields.context);
     return retriever;
 }
 
-static void setRetriever(JNIEnv* env, jobject thiz, MediaMetadataRetriever* retriever)
+static void setRetriever(JNIEnv* env, jobject thiz, const sp<MediaMetadataRetriever> &retriever)
 {
     // No lock is needed, since it is called internally by other methods that are protected
-    env->SetLongField(thiz, fields.context, (jlong) retriever);
+
+    if (retriever != NULL) {
+        retriever->incStrong(thiz);
+    }
+    sp<MediaMetadataRetriever> old = getRetriever(env, thiz);
+    if (old != NULL) {
+        old->decStrong(thiz);
+    }
+
+    env->SetLongField(thiz, fields.context, (jlong) retriever.get());
 }
 
 static void
@@ -87,7 +96,7 @@
         jobjectArray keys, jobjectArray values) {
 
     ALOGV("setDataSource");
-    MediaMetadataRetriever* retriever = getRetriever(env, thiz);
+    sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
     if (retriever == 0) {
         jniThrowException(
                 env,
@@ -146,7 +155,7 @@
 static void android_media_MediaMetadataRetriever_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
 {
     ALOGV("setDataSource");
-    MediaMetadataRetriever* retriever = getRetriever(env, thiz);
+    sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
     if (retriever == 0) {
         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
         return;
@@ -175,7 +184,7 @@
 static void android_media_MediaMetadataRetriever_setDataSourceCallback(JNIEnv *env, jobject thiz, jobject dataSource)
 {
     ALOGV("setDataSourceCallback");
-    MediaMetadataRetriever* retriever = getRetriever(env, thiz);
+    sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
     if (retriever == 0) {
         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
         return;
@@ -325,7 +334,7 @@
 {
     ALOGV("getFrameAtTime: %lld us option: %d dst width: %d heigh: %d",
             (long long)timeUs, option, dst_width, dst_height);
-    MediaMetadataRetriever* retriever = getRetriever(env, thiz);
+    sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
     if (retriever == 0) {
         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
         return NULL;
@@ -349,7 +358,7 @@
         JNIEnv *env, jobject thiz, jint index)
 {
     ALOGV("getImageAtIndex: index %d", index);
-    MediaMetadataRetriever* retriever = getRetriever(env, thiz);
+    sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
     if (retriever == 0) {
         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
         return NULL;
@@ -373,7 +382,7 @@
         JNIEnv *env, jobject thiz, jint frameIndex, jint numFrames)
 {
     ALOGV("getFrameAtIndex: frameIndex %d, numFrames %d", frameIndex, numFrames);
-    MediaMetadataRetriever* retriever = getRetriever(env, thiz);
+    sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
     if (retriever == 0) {
         jniThrowException(env,
                 "java/lang/IllegalStateException", "No retriever available");
@@ -411,7 +420,7 @@
         JNIEnv *env, jobject thiz, jint pictureType)
 {
     ALOGV("getEmbeddedPicture: %d", pictureType);
-    MediaMetadataRetriever* retriever = getRetriever(env, thiz);
+    sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
     if (retriever == 0) {
         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
         return NULL;
@@ -446,7 +455,7 @@
 static jobject android_media_MediaMetadataRetriever_extractMetadata(JNIEnv *env, jobject thiz, jint keyCode)
 {
     ALOGV("extractMetadata");
-    MediaMetadataRetriever* retriever = getRetriever(env, thiz);
+    sp<MediaMetadataRetriever> retriever = getRetriever(env, thiz);
     if (retriever == 0) {
         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
         return NULL;
@@ -464,9 +473,7 @@
 {
     ALOGV("release");
     Mutex::Autolock lock(sLock);
-    MediaMetadataRetriever* retriever = getRetriever(env, thiz);
-    delete retriever;
-    setRetriever(env, thiz, (MediaMetadataRetriever*) 0);
+    setRetriever(env, thiz, NULL);
 }
 
 static void android_media_MediaMetadataRetriever_native_finalize(JNIEnv *env, jobject thiz)
@@ -533,7 +540,7 @@
 static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz)
 {
     ALOGV("native_setup");
-    MediaMetadataRetriever* retriever = new MediaMetadataRetriever();
+    sp<MediaMetadataRetriever> retriever = new MediaMetadataRetriever();
     if (retriever == 0) {
         jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
         return;
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index fce5dd9..7728f66 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -363,6 +363,26 @@
     }
 
     /**
+     * Check if {@param packageName} is restricted by the profile or device owner from using
+     * metered data.
+     *
+     * @return EnforcedAdmin object containing the enforced admin component and admin user details,
+     * or {@code null} if the {@param packageName} is not restricted.
+     */
+    public static EnforcedAdmin checkIfMeteredDataRestricted(Context context,
+            String packageName, int userId) {
+        final EnforcedAdmin enforcedAdmin = getProfileOrDeviceOwner(context, userId);
+        if (enforcedAdmin == null) {
+            return null;
+        }
+
+        final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        return dpm.isMeteredDataDisabledForUser(enforcedAdmin.component, packageName, userId)
+                ? enforcedAdmin : null;
+    }
+
+    /**
      * Checks if {@link android.app.admin.DevicePolicyManager#setAutoTimeRequired} is enforced
      * on the device.
      *
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 5c73d54..3a0ae9f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -48,6 +48,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.support.annotation.VisibleForTesting;
 import android.text.format.Formatter;
 import android.util.IconDrawableFactory;
 import android.util.Log;
@@ -1282,7 +1283,8 @@
         // A location where extra info can be placed to be used by custom filters.
         public Object extraInfo;
 
-        AppEntry(Context context, ApplicationInfo info, long id) {
+        @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
+        public AppEntry(Context context, ApplicationInfo info, long id) {
             apkFile = new File(info.sourceDir);
             this.id = id;
             this.info = info;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 754b881..e11017c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -32,7 +32,6 @@
 import android.net.NetworkScoreManager;
 import android.net.NetworkScorerAppData;
 import android.net.ScoredNetwork;
-import android.net.WifiKey;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
@@ -43,6 +42,7 @@
 import android.net.wifi.WifiNetworkScoreCache;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.os.Bundle;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -52,6 +52,7 @@
 import android.text.SpannableString;
 import android.text.TextUtils;
 import android.text.style.TtsSpan;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -60,11 +61,11 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 
 
@@ -91,6 +92,9 @@
      */
     public static final int HIGHER_FREQ_5GHZ = 5900;
 
+    /** The key which identifies this AccessPoint grouping. */
+    private String mKey;
+
     @IntDef({Speed.NONE, Speed.SLOW, Speed.MODERATE, Speed.FAST, Speed.VERY_FAST})
     @Retention(RetentionPolicy.SOURCE)
     public @interface Speed {
@@ -116,14 +120,8 @@
         int VERY_FAST = 30;
     }
 
-    /**
-     * Experimental: we should be able to show the user the list of BSSIDs and bands
-     *  for that SSID.
-     *  For now this data is used only with Verbose Logging so as to show the band and number
-     *  of BSSIDs on which that network is seen.
-     */
-    private final ConcurrentHashMap<String, ScanResult> mScanResultCache =
-            new ConcurrentHashMap<String, ScanResult>(32);
+    /** The underlying set of scan results comprising this AccessPoint. */
+    private final ArraySet<ScanResult> mScanResults = new ArraySet<>();
 
     /**
      * Map of BSSIDs to scored networks for individual bssids.
@@ -133,17 +131,13 @@
      */
     private final Map<String, TimestampedScoredNetwork> mScoredNetworkCache = new HashMap<>();
 
-    /** Maximum age of scan results to hold onto while actively scanning. **/
-    private static final long MAX_SCAN_RESULT_AGE_MILLIS = 25000;
-
     static final String KEY_NETWORKINFO = "key_networkinfo";
     static final String KEY_WIFIINFO = "key_wifiinfo";
-    static final String KEY_SCANRESULT = "key_scanresult";
     static final String KEY_SSID = "key_ssid";
     static final String KEY_SECURITY = "key_security";
     static final String KEY_SPEED = "key_speed";
     static final String KEY_PSKTYPE = "key_psktype";
-    static final String KEY_SCANRESULTCACHE = "key_scanresultcache";
+    static final String KEY_SCANRESULTS = "key_scanresults";
     static final String KEY_SCOREDNETWORKCACHE = "key_scorednetworkcache";
     static final String KEY_CONFIG = "key_config";
     static final String KEY_FQDN = "key_fqdn";
@@ -216,7 +210,10 @@
 
     public AccessPoint(Context context, Bundle savedState) {
         mContext = context;
-        mConfig = savedState.getParcelable(KEY_CONFIG);
+
+        if (savedState.containsKey(KEY_CONFIG)) {
+            mConfig = savedState.getParcelable(KEY_CONFIG);
+        }
         if (mConfig != null) {
             loadConfig(mConfig);
         }
@@ -236,12 +233,11 @@
         if (savedState.containsKey(KEY_NETWORKINFO)) {
             mNetworkInfo = savedState.getParcelable(KEY_NETWORKINFO);
         }
-        if (savedState.containsKey(KEY_SCANRESULTCACHE)) {
-            ArrayList<ScanResult> scanResultArrayList =
-                    savedState.getParcelableArrayList(KEY_SCANRESULTCACHE);
-            mScanResultCache.clear();
-            for (ScanResult result : scanResultArrayList) {
-                mScanResultCache.put(result.BSSID, result);
+        if (savedState.containsKey(KEY_SCANRESULTS)) {
+            Parcelable[] scanResults = savedState.getParcelableArray(KEY_SCANRESULTS);
+            mScanResults.clear();
+            for (Parcelable result : scanResults) {
+                mScanResults.add((ScanResult) result);
             }
         }
         if (savedState.containsKey(KEY_SCOREDNETWORKCACHE)) {
@@ -268,8 +264,10 @@
         }
         update(mConfig, mInfo, mNetworkInfo);
 
-        // Do not evict old scan results on initial creation
+        // Calculate required fields
+        updateKey();
         updateRssi();
+
         mId = sLastId.incrementAndGet();
     }
 
@@ -295,30 +293,75 @@
         copyFrom(other);
     }
 
-    AccessPoint(Context context, ScanResult result) {
+    AccessPoint(Context context, Collection<ScanResult> results) {
         mContext = context;
-        initWithScanResult(result);
+
+        if (results.isEmpty()) {
+            throw new IllegalArgumentException("Cannot construct with an empty ScanResult list");
+        }
+        mScanResults.addAll(results);
+
+        // Information derived from scan results
+        ScanResult firstResult = results.iterator().next();
+        ssid = firstResult.SSID;
+        bssid = firstResult.BSSID;
+        security = getSecurity(firstResult);
+        if (security == SECURITY_PSK) {
+            pskType = getPskType(firstResult);
+        }
+        updateKey();
+        updateRssi();
+
+        // Passpoint Info
+        mIsCarrierAp = firstResult.isCarrierAp;
+        mCarrierApEapType = firstResult.carrierApEapType;
+        mCarrierName = firstResult.carrierName;
+
         mId = sLastId.incrementAndGet();
     }
 
+    @VisibleForTesting void loadConfig(WifiConfiguration config) {
+        ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
+        bssid = config.BSSID;
+        security = getSecurity(config);
+        updateKey();
+        networkId = config.networkId;
+        mConfig = config;
+    }
+
+    /** Updates {@link #mKey} and should only called upon object creation/initialization. */
+    private void updateKey() {
+        // TODO(sghuman): Consolidate Key logic on ScanResultMatchInfo
+
+        StringBuilder builder = new StringBuilder();
+
+        if (TextUtils.isEmpty(getSsidStr())) {
+            builder.append(getBssid());
+        } else {
+            builder.append(getSsidStr());
+        }
+
+        builder.append(',').append(getSecurity());
+        mKey = builder.toString();
+    }
+
     /**
      * Copy accesspoint information. NOTE: We do not copy tag information because that is never
      * set on the internal copy.
-     * @param that
      */
     void copyFrom(AccessPoint that) {
-        that.evictOldScanResults();
         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.mScanResultCache.clear();
-        this.mScanResultCache.putAll(that.mScanResultCache);
+        this.mScanResults.clear();
+        this.mScanResults.addAll(that.mScanResults);
         this.mScoredNetworkCache.clear();
         this.mScoredNetworkCache.putAll(that.mScoredNetworkCache);
         this.mId = that.mId;
@@ -426,7 +469,7 @@
 
         if (WifiTracker.sVerboseLogging) {
             builder.append(",rssi=").append(mRssi);
-            builder.append(",scan cache size=").append(mScanResultCache.size());
+            builder.append(",scan cache size=").append(mScanResults.size());
         }
 
         return builder.append(')').toString();
@@ -468,7 +511,7 @@
      */
     private boolean updateScores(WifiNetworkScoreCache scoreCache, long maxScoreCacheAgeMillis) {
         long nowMillis = SystemClock.elapsedRealtime();
-        for (ScanResult result : mScanResultCache.values()) {
+        for (ScanResult result : mScanResults) {
             ScoredNetwork score = scoreCache.getScoredNetwork(result);
             if (score == null) {
                 continue;
@@ -555,7 +598,7 @@
                 mIsScoredNetworkMetered |= score.meteredHint;
             }
         } else {
-            for (ScanResult result : mScanResultCache.values()) {
+            for (ScanResult result : mScanResults) {
                 ScoredNetwork score = scoreCache.getScoredNetwork(result);
                 if (score == null) {
                     continue;
@@ -566,19 +609,21 @@
         return oldMetering == mIsScoredNetworkMetered;
     }
 
-    private void evictOldScanResults() {
-        long nowMs = SystemClock.elapsedRealtime();
-        for (Iterator<ScanResult> iter = mScanResultCache.values().iterator(); iter.hasNext(); ) {
-            ScanResult result = iter.next();
-            // result timestamp is in microseconds
-            if (nowMs - result.timestamp / 1000 > MAX_SCAN_RESULT_AGE_MILLIS) {
-                iter.remove();
-            }
+    public static String getKey(ScanResult result) {
+        StringBuilder builder = new StringBuilder();
+
+        if (TextUtils.isEmpty(result.SSID)) {
+            builder.append(result.BSSID);
+        } else {
+            builder.append(result.SSID);
         }
+
+        builder.append(',').append(getSecurity(result));
+        return builder.toString();
     }
 
-    public boolean matches(ScanResult result) {
-        return ssid.equals(result.SSID) && security == getSecurity(result);
+    public String getKey() {
+        return mKey;
     }
 
     public boolean matches(WifiConfiguration config) {
@@ -622,9 +667,12 @@
         return mRssi;
     }
 
-    public ConcurrentHashMap<String, ScanResult> getScanResults() {
-        return mScanResultCache;
-    }
+    /**
+     * Returns the underlying scan result set.
+     *
+     * <p>Callers should not modify this set.
+     */
+    public Set<ScanResult> getScanResults() { return mScanResults; }
 
     public Map<String, TimestampedScoredNetwork> getScoredNetworkCache() {
         return mScoredNetworkCache;
@@ -645,7 +693,7 @@
         }
 
         int rssi = UNREACHABLE_RSSI;
-        for (ScanResult result : mScanResultCache.values()) {
+        for (ScanResult result : mScanResults) {
             if (result.level > rssi) {
                 rssi = result.level;
             }
@@ -853,7 +901,6 @@
         }
 
         if (WifiTracker.sVerboseLogging) {
-            evictOldScanResults();
             summary.append(WifiUtils.buildLoggingSummary(this, config));
         }
 
@@ -950,28 +997,6 @@
         mConfig.allowedKeyManagement.set(KeyMgmt.NONE);
     }
 
-    void loadConfig(WifiConfiguration config) {
-        ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
-        bssid = config.BSSID;
-        security = getSecurity(config);
-        networkId = config.networkId;
-        mConfig = config;
-    }
-
-    private void initWithScanResult(ScanResult result) {
-        ssid = result.SSID;
-        bssid = result.BSSID;
-        security = getSecurity(result);
-        if (security == SECURITY_PSK)
-            pskType = getPskType(result);
-
-        mScanResultCache.put(result.BSSID, result);
-        updateRssi();
-        mIsCarrierAp = result.isCarrierAp;
-        mCarrierApEapType = result.carrierApEapType;
-        mCarrierName = result.carrierName;
-    }
-
     public void saveWifiState(Bundle savedState) {
         if (ssid != null) savedState.putString(KEY_SSID, getSsidStr());
         savedState.putInt(KEY_SECURITY, security);
@@ -979,9 +1004,8 @@
         savedState.putInt(KEY_PSKTYPE, pskType);
         if (mConfig != null) savedState.putParcelable(KEY_CONFIG, mConfig);
         savedState.putParcelable(KEY_WIFIINFO, mInfo);
-        evictOldScanResults();
-        savedState.putParcelableArrayList(KEY_SCANRESULTCACHE,
-                new ArrayList<ScanResult>(mScanResultCache.values()));
+        savedState.putParcelableArray(KEY_SCANRESULTS,
+                mScanResults.toArray(new Parcelable[mScanResults.size()]));
         savedState.putParcelableArrayList(KEY_SCOREDNETWORKCACHE,
                 new ArrayList<>(mScoredNetworkCache.values()));
         if (mNetworkInfo != null) {
@@ -1003,49 +1027,58 @@
     }
 
     /**
-     * Update the AP with the given scan result.
+     * Sets {@link #mScanResults} to the given collection.
      *
-     * @param result the ScanResult to add to the AccessPoint scan cache
-     * @param evictOldScanResults whether stale scan results should be removed
-     *         from the cache during this update process
-     * @return true if the scan result update caused a change in state which would impact ranking
-     *     or AccessPoint rendering (e.g. wifi level, security)
+     * @param scanResults a collection of scan results to add to the internal set
+     * @throws IllegalArgumentException if any of the given ScanResults did not belong to this AP
      */
-    boolean update(ScanResult result, boolean evictOldScanResults) {
-        if (matches(result)) {
-            int oldLevel = getLevel();
+    void setScanResults(Collection<ScanResult> scanResults) {
 
-            /* Add or update the scan result for the BSSID */
-            mScanResultCache.put(result.BSSID, result);
-            if (evictOldScanResults) evictOldScanResults();
-            updateRssi();
-            int newLevel = getLevel();
-
-            if (newLevel > 0 && newLevel != oldLevel) {
-                // Only update labels on visible rssi changes
-                updateSpeed();
-                if (mAccessPointListener != null) {
-                    mAccessPointListener.onLevelChanged(this);
-                }
+        // Validate scan results are for current AP only
+        String key = getKey();
+        for (ScanResult result : scanResults) {
+            String scanResultKey = AccessPoint.getKey(result);
+            if (!mKey.equals(scanResultKey)) {
+                throw new IllegalArgumentException(
+                        String.format("ScanResult %s\nkey of %s did not match current AP key %s",
+                                      result, scanResultKey, key));
             }
+        }
+
+
+        int oldLevel = getLevel();
+        mScanResults.clear();
+        mScanResults.addAll(scanResults);
+        updateRssi();
+        int newLevel = getLevel();
+
+        // If newLevel is 0, there will be no displayed Preference since the AP is unreachable
+        if (newLevel > 0 && newLevel != oldLevel) {
+            // Only update labels on visible rssi changes
+            updateSpeed();
+            if (mAccessPointListener != null) {
+                mAccessPointListener.onLevelChanged(this);
+            }
+        }
+
+        if (mAccessPointListener != null) {
+            mAccessPointListener.onAccessPointChanged(this);
+        }
+
+        if (!scanResults.isEmpty()) {
+            ScanResult result = scanResults.iterator().next();
+
             // This flag only comes from scans, is not easily saved in config
             if (security == SECURITY_PSK) {
                 pskType = getPskType(result);
             }
 
-            if (mAccessPointListener != null) {
-                mAccessPointListener.onAccessPointChanged(this);
-            }
-
             // The carrier info in the ScanResult is set by the platform based on the SSID and will
             // always be the same for all matching scan results.
             mIsCarrierAp = result.isCarrierAp;
             mCarrierApEapType = result.carrierApEapType;
             mCarrierName = result.carrierName;
-
-            return true;
         }
-        return false;
     }
 
     /** Attempt to update the AccessPoint and return true if an update occurred. */
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index dd55188..109eb97 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -77,19 +77,6 @@
     private int mDefaultIconResId;
     private int mWifiSpeed = Speed.NONE;
 
-    public static String generatePreferenceKey(AccessPoint accessPoint) {
-        StringBuilder builder = new StringBuilder();
-
-        if (TextUtils.isEmpty(accessPoint.getSsidStr())) {
-            builder.append(accessPoint.getBssid());
-        } else {
-            builder.append(accessPoint.getSsidStr());
-        }
-
-        builder.append(',').append(accessPoint.getSecurity());
-        return builder.toString();
-    }
-
     @Nullable
     private static StateListDrawable getFrictionStateListDrawable(Context context) {
         TypedArray frictionSld;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
index 3dec1d3..2993a0d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
@@ -23,6 +23,7 @@
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
 import android.os.Bundle;
+import android.os.Parcelable;
 import android.support.annotation.Keep;
 
 import com.android.settingslib.wifi.AccessPoint.Speed;
@@ -58,7 +59,7 @@
     private String mCarrierName = null;
 
     Context mContext;
-    private ArrayList<ScanResult> mScanResultCache;
+    private ArrayList<ScanResult> mScanResults;
     private ArrayList<TimestampedScoredNetwork> mScoredNetworkCache;
 
     @Keep
@@ -84,8 +85,9 @@
         if (mProviderFriendlyName != null) {
             bundle.putString(AccessPoint.KEY_PROVIDER_FRIENDLY_NAME, mProviderFriendlyName);
         }
-        if (mScanResultCache != null) {
-            bundle.putParcelableArrayList(AccessPoint.KEY_SCANRESULTCACHE, mScanResultCache);
+        if (mScanResults != null) {
+            bundle.putParcelableArray(AccessPoint.KEY_SCANRESULTS,
+                    mScanResults.toArray(new Parcelable[mScanResults.size()]));
         }
         if (mScoredNetworkCache != null) {
             bundle.putParcelableArrayList(AccessPoint.KEY_SCOREDNETWORKCACHE, mScoredNetworkCache);
@@ -229,8 +231,8 @@
         return this;
     }
 
-    public TestAccessPointBuilder setScanResultCache(ArrayList<ScanResult> scanResultCache) {
-        mScanResultCache = scanResultCache;
+    public TestAccessPointBuilder setScanResults(ArrayList<ScanResult> scanResults) {
+        mScanResults = scanResults;
         return this;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index c56e1da..1ac56a9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -39,11 +39,13 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.support.annotation.GuardedBy;
 import android.support.annotation.NonNull;
 import android.support.annotation.VisibleForTesting;
 import android.text.format.DateUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
@@ -78,6 +80,9 @@
      */
     private static final long DEFAULT_MAX_CACHED_SCORE_AGE_MILLIS = 20 * DateUtils.MINUTE_IN_MILLIS;
 
+    /** Maximum age of scan results to hold onto while actively scanning. **/
+    private static final long MAX_SCAN_RESULT_AGE_MILLIS = 25000;
+
     private static final String TAG = "WifiTracker";
     private static final boolean DBG() {
         return Log.isLoggable(TAG, Log.DEBUG);
@@ -142,6 +147,8 @@
             = new AccessPointListenerAdapter();
 
     private final HashMap<String, Integer> mSeenBssids = new HashMap<>();
+
+    // TODO(sghuman): Change this to be keyed on AccessPoint.getKey
     private final HashMap<String, ScanResult> mScanResultCache = new HashMap<>();
     private Integer mScanId = 0;
 
@@ -455,34 +462,42 @@
     }
 
     private Collection<ScanResult> updateScanResultCache(final List<ScanResult> newResults) {
-        mScanId++;
+        // TODO(sghuman): Delete this and replace it with the Map of Ap Keys to ScanResults
         for (ScanResult newResult : newResults) {
             if (newResult.SSID == null || newResult.SSID.isEmpty()) {
                 continue;
             }
             mScanResultCache.put(newResult.BSSID, newResult);
-            mSeenBssids.put(newResult.BSSID, mScanId);
         }
 
-        if (mScanId > NUM_SCANS_TO_CONFIRM_AP_LOSS) {
-            if (DBG()) Log.d(TAG, "------ Dumping SSIDs that were expired on this scan ------");
-            Integer threshold = mScanId - NUM_SCANS_TO_CONFIRM_AP_LOSS;
-            for (Iterator<Map.Entry<String, Integer>> it = mSeenBssids.entrySet().iterator();
-                    it.hasNext(); /* nothing */) {
-                Map.Entry<String, Integer> e = it.next();
-                if (e.getValue() < threshold) {
-                    ScanResult result = mScanResultCache.get(e.getKey());
-                    if (DBG()) Log.d(TAG, "Removing " + e.getKey() + ":(" + result.SSID + ")");
-                    mScanResultCache.remove(e.getKey());
-                    it.remove();
-                }
-            }
-            if (DBG()) Log.d(TAG, "---- Done Dumping SSIDs that were expired on this scan ----");
+        // Don't evict old results if no new scan results
+        if (!mStaleScanResults) {
+            evictOldScans();
         }
 
+        // TODO(sghuman): Update a Map<ApKey, List<ScanResults>> variable to be reused later after
+        // double threads have been removed.
+
         return mScanResultCache.values();
     }
 
+    /**
+     * Remove old scan results from the cache.
+     *
+     * <p>Should only ever be invoked from {@link #updateScanResultCache(List)} when
+     * {@link #mStaleScanResults} is false.
+     */
+    private void evictOldScans() {
+        long nowMs = SystemClock.elapsedRealtime();
+        for (Iterator<ScanResult> iter = mScanResultCache.values().iterator(); iter.hasNext(); ) {
+            ScanResult result = iter.next();
+            // result timestamp is in microseconds
+            if (nowMs - result.timestamp / 1000 > MAX_SCAN_RESULT_AGE_MILLIS) {
+                iter.remove();
+            }
+        }
+    }
+
     private WifiConfiguration getWifiConfigurationForNetworkId(
             int networkId, final List<WifiConfiguration> configs) {
         if (configs != null) {
@@ -541,10 +556,12 @@
 
     /* Lookup table to more quickly update AccessPoints by only considering objects with the
      * correct SSID.  Maps SSID -> List of AccessPoints with the given SSID.  */
-        Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>();
+        Multimap<String, AccessPoint> existingApMap = new Multimap<String, AccessPoint>();
 
         final Collection<ScanResult> results = updateScanResultCache(newScanResults);
 
+        // TODO(sghuman): This entire block only exists to populate the WifiConfiguration for
+        // APs, remove and refactor
         if (configs != null) {
             for (WifiConfiguration config : configs) {
                 if (config.selfAdded && config.numAssociation == 0) {
@@ -568,7 +585,7 @@
                         accessPoint.setUnreachable();
                     }
                     accessPoints.add(accessPoint);
-                    apMap.put(accessPoint.getSsidStr(), accessPoint);
+                    existingApMap.put(accessPoint.getSsidStr(), accessPoint);
                 } else {
                     // If we aren't using saved networks, drop them into the cache so that
                     // we have access to their saved info.
@@ -579,6 +596,9 @@
 
         final List<NetworkKey> scoresToRequest = new ArrayList<>();
         if (results != null) {
+            // TODO(sghuman): Move this loop to updateScanResultCache and make instance variable
+            // after double handlers are removed.
+            ArrayMap<String, List<ScanResult>> scanResultsByApKey = new ArrayMap<>();
             for (ScanResult result : results) {
                 // Ignore hidden and ad-hoc networks.
                 if (result.SSID == null || result.SSID.length() == 0 ||
@@ -591,27 +611,45 @@
                     scoresToRequest.add(key);
                 }
 
-                boolean found = false;
-                for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {
-                    // We want to evict old scan results if are current results are not stale
-                    if (accessPoint.update(result, !mStaleScanResults)) {
-                        found = true;
-                        break;
-                    }
+                String apKey = AccessPoint.getKey(result);
+                List<ScanResult> resultList;
+                if (scanResultsByApKey.containsKey(apKey)) {
+                    resultList = scanResultsByApKey.get(apKey);
+                } else {
+                    resultList = new ArrayList<>();
+                    scanResultsByApKey.put(apKey, resultList);
                 }
-                if (!found && mIncludeScans) {
-                    AccessPoint accessPoint = getCachedOrCreate(result, cachedAccessPoints);
+
+                resultList.add(result);
+            }
+
+            for (Map.Entry<String, List<ScanResult>> entry : scanResultsByApKey.entrySet()) {
+                // List can not be empty as it is dynamically constructed on each iteration
+                ScanResult firstResult = entry.getValue().get(0);
+                boolean found = false;
+                for (AccessPoint accessPoint : existingApMap.getAll(firstResult.SSID)) {
+                    accessPoint.setScanResults(entry.getValue());
+                    found = true;
+                    break;
+                }
+
+                // Only create a new AP / add to the list if it wasn't already in the saved configs
+                if (!found) {
+                    AccessPoint accessPoint =
+                            getCachedOrCreate(entry.getValue(), cachedAccessPoints);
                     if (mLastInfo != null && mLastNetworkInfo != null) {
                         accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
                     }
 
-                    if (result.isPasspointNetwork()) {
+                    // TODO(sghuman): Move isPasspointNetwork logic into AccessPoint.java
+                    if (firstResult.isPasspointNetwork()) {
                         // Retrieve a WifiConfiguration for a Passpoint provider that matches
                         // the given ScanResult.  This is used for showing that a given AP
                         // (ScanResult) is available via a Passpoint provider (provider friendly
                         // name).
                         try {
-                            WifiConfiguration config = mWifiManager.getMatchingWifiConfig(result);
+                            WifiConfiguration config =
+                                    mWifiManager.getMatchingWifiConfig(firstResult);
                             if (config != null) {
                                 accessPoint.update(config);
                             }
@@ -621,7 +659,6 @@
                     }
 
                     accessPoints.add(accessPoint);
-                    apMap.put(accessPoint.getSsidStr(), accessPoint);
                 }
             }
         }
@@ -662,17 +699,18 @@
     }
 
     @VisibleForTesting
-    AccessPoint getCachedOrCreate(ScanResult result, List<AccessPoint> cache) {
+    AccessPoint getCachedOrCreate(
+            List<ScanResult> scanResults,
+            List<AccessPoint> cache) {
         final int N = cache.size();
         for (int i = 0; i < N; i++) {
-            if (cache.get(i).matches(result)) {
+            if (cache.get(i).getKey().equals(AccessPoint.getKey(scanResults.get(0)))) {
                 AccessPoint ret = cache.remove(i);
-                // evict old scan results only if we have fresh results
-                ret.update(result, !mStaleScanResults);
+                ret.setScanResults(scanResults);
                 return ret;
             }
         }
-        final AccessPoint accessPoint = new AccessPoint(mContext, result);
+        final AccessPoint accessPoint = new AccessPoint(mContext, scanResults);
         accessPoint.setListener(mAccessPointListenerAdapter);
         return accessPoint;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 932c6fd..fd48eea 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -109,7 +109,7 @@
 
         // TODO: sort list by RSSI or age
         long nowMs = SystemClock.elapsedRealtime();
-        for (ScanResult result : accessPoint.getScanResults().values()) {
+        for (ScanResult result : accessPoint.getScanResults()) {
             if (result.frequency >= AccessPoint.LOWER_FREQ_5GHZ
                     && result.frequency <= AccessPoint.HIGHER_FREQ_5GHZ) {
                 // Strictly speaking: [4915, 5825]
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 ec594a6..1440311 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
@@ -40,6 +40,7 @@
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.hotspot2.pps.HomeSp;
 import android.os.Bundle;
+import android.os.Parcelable;
 import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -277,7 +278,8 @@
         scanResult.BSSID = "bssid";
         scanResult.timestamp = SystemClock.elapsedRealtime() * 1000;
         scanResult.capabilities = "";
-        assertThat(ap.update(scanResult, true /* evict old scan results */)).isTrue();
+
+        ap.setScanResults(Collections.singletonList(scanResult));
 
         assertThat(ap.getRssi()).isEqualTo(expectedRssi);
     }
@@ -477,7 +479,7 @@
         result.carrierApEapType = WifiEnterpriseConfig.Eap.SIM;
         result.carrierName = carrierName;
 
-        AccessPoint ap = new AccessPoint(mContext, result);
+        AccessPoint ap = new AccessPoint(mContext, Collections.singletonList(result));
         assertThat(ap.getSummary()).isEqualTo(String.format(mContext.getString(
                 R.string.available_via_carrier), carrierName));
         assertThat(ap.isCarrierAp()).isEqualTo(true);
@@ -513,7 +515,7 @@
     }
 
     @Test
-    public void testUpdateScanResultWithCarrierInfo() {
+    public void testSetScanResultWithCarrierInfo() {
         String ssid = "ssid";
         AccessPoint ap = new TestAccessPointBuilder(mContext).setSsid(ssid).build();
         assertThat(ap.isCarrierAp()).isEqualTo(false);
@@ -529,8 +531,9 @@
         scanResult.isCarrierAp = true;
         scanResult.carrierApEapType = carrierApEapType;
         scanResult.carrierName = carrierName;
-        assertThat(ap.update(scanResult, true /* evictOldScanresults */)).isTrue();
 
+
+        ap.setScanResults(Collections.singletonList(scanResult));
         assertThat(ap.isCarrierAp()).isEqualTo(true);
         assertThat(ap.getCarrierApEapType()).isEqualTo(carrierApEapType);
         assertThat(ap.getCarrierName()).isEqualTo(carrierName);
@@ -552,7 +555,9 @@
 
     private AccessPoint createAccessPointWithScanResultCache() {
         Bundle bundle = new Bundle();
-        bundle.putParcelableArrayList(AccessPoint.KEY_SCANRESULTCACHE, SCAN_RESULTS);
+        bundle.putParcelableArray(
+                AccessPoint.KEY_SCANRESULTS,
+                SCAN_RESULTS.toArray(new Parcelable[SCAN_RESULTS.size()]));
         return new AccessPoint(mContext, bundle);
     }
 
@@ -903,7 +908,7 @@
                         .setActive(true)
                         .setNetworkId(networkId)
                         .setSsid(TEST_SSID)
-                        .setScanResultCache(scanResults)
+                        .setScanResults(scanResults)
                         .setWifiInfo(info)
                         .build();
 
@@ -990,7 +995,7 @@
                 .setActive(true)
                 .setScoredNetworkCache(
                         new ArrayList(Arrays.asList(recentScore)))
-                .setScanResultCache(SCAN_RESULTS)
+                .setScanResults(SCAN_RESULTS)
                 .build();
 
         when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
@@ -1018,7 +1023,7 @@
                 .setActive(true)
                 .setScoredNetworkCache(
                         new ArrayList(Arrays.asList(recentScore)))
-                .setScanResultCache(SCAN_RESULTS)
+                .setScanResults(SCAN_RESULTS)
                 .build();
 
         int newSpeed = Speed.MODERATE;
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 6615b8c..b36dda9 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
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -57,9 +58,9 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.test.filters.FlakyTest;
 
 import org.junit.After;
 import org.junit.Before;
@@ -76,7 +77,9 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.BitSet;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -399,7 +402,8 @@
         WifiTracker tracker = new WifiTracker(
                 InstrumentationRegistry.getTargetContext(), null, true, true);
 
-        AccessPoint result = tracker.getCachedOrCreate(scanResult, new ArrayList<AccessPoint>());
+        AccessPoint result = tracker.getCachedOrCreate(
+                Collections.singletonList(scanResult), new ArrayList<AccessPoint>());
         assertTrue(result.mAccessPointListener != null);
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
index a4c821f..3fee16b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/AccessPointPreferenceTest.java
@@ -61,34 +61,6 @@
     }
 
     @Test
-    public void generatePreferenceKey_returnsSsidPlusSecurity() {
-        String ssid = "ssid";
-        String bssid = "00:00:00:00:00:00";
-        int security = AccessPoint.SECURITY_WEP;
-        String expectedKey = ssid + ',' + security;
-
-        TestAccessPointBuilder builder = new TestAccessPointBuilder(mContext);
-        builder.setBssid(bssid).setSsid(ssid).setSecurity(security);
-
-        assertThat(AccessPointPreference.generatePreferenceKey(builder.build()))
-                .isEqualTo(expectedKey);
-    }
-
-    @Test
-    public void generatePreferenceKey_emptySsidReturnsBssidPlusSecurity() {
-        String ssid = "";
-        String bssid = "00:00:00:00:00:00";
-        int security = AccessPoint.SECURITY_WEP;
-        String expectedKey = bssid + ',' + security;
-
-        TestAccessPointBuilder builder = new TestAccessPointBuilder(mContext);
-        builder.setBssid(bssid).setSsid(ssid).setSecurity(security);
-
-        assertThat(AccessPointPreference.generatePreferenceKey(builder.build()))
-                .isEqualTo(expectedKey);
-    }
-
-    @Test
     public void refresh_openNetwork_updateContentDescription() {
         final String ssid = "ssid";
         final String summary = "connected";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index c5795d3..9310b73 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -29,6 +29,7 @@
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiNetworkScoreCache;
 import android.os.Bundle;
+import android.os.Parcelable;
 import android.os.SystemClock;
 import android.text.format.DateUtils;
 
@@ -72,7 +73,8 @@
 
         Bundle bundle = new Bundle();
         ArrayList<ScanResult> scanResults = buildScanResultCache();
-        bundle.putParcelableArrayList(AccessPoint.KEY_SCANRESULTCACHE, scanResults);
+        bundle.putParcelableArray(AccessPoint.KEY_SCANRESULTS,
+                                  scanResults.toArray(new Parcelable[scanResults.size()]));
         AccessPoint ap = new AccessPoint(mContext, bundle);
 
         when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 1fc36be..9613a6a 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -90,7 +90,6 @@
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.GET_TOP_ACTIVITY_INFO" />
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
-    <uses-permission android:name="android.permission.START_ACTIVITY_AS_CALLER" />
     <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
     <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
 
@@ -560,22 +559,6 @@
             </intent-filter>
         </activity>
 
-        <activity android:name=".chooser.ChooserActivity"
-                android:theme="@*android:style/Theme.NoDisplay"
-                android:finishOnCloseSystemDialogs="true"
-                android:excludeFromRecents="true"
-                android:documentLaunchMode="never"
-                android:relinquishTaskIdentity="true"
-                android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
-                android:process=":ui"
-                android:visibleToInstantApps="true">
-            <intent-filter>
-                <action android:name="android.intent.action.CHOOSER_UI" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.VOICE" />
-            </intent-filter>
-        </activity>
-
         <!-- Doze with notifications, run in main sysui process for every user  -->
         <service
             android:name=".doze.DozeService"
diff --git a/packages/SystemUI/shared/Android.mk b/packages/SystemUI/shared/Android.mk
index 5f75f7d..21b0ed8 100644
--- a/packages/SystemUI/shared/Android.mk
+++ b/packages/SystemUI/shared/Android.mk
@@ -30,7 +30,7 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_PACKAGE_NAME := SystemUISharedLib
+LOCAL_PACKAGE_NAME := SysUISharedLib
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_JAVA_LIBRARIES := SystemUISharedLib
@@ -39,4 +39,4 @@
 
 include $(BUILD_PACKAGE)
 
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 8666b0c..1ae06d7 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -119,18 +119,10 @@
         addView(mBatteryIconView, mlp);
 
         updateShowPercent();
-
-        Context dualToneDarkTheme = new ContextThemeWrapper(context,
-                Utils.getThemeAttr(context, R.attr.darkIconTheme));
-        Context dualToneLightTheme = new ContextThemeWrapper(context,
-                Utils.getThemeAttr(context, R.attr.lightIconTheme));
-        mDarkModeBackgroundColor = Utils.getColorAttr(dualToneDarkTheme, R.attr.backgroundColor);
-        mDarkModeFillColor = Utils.getColorAttr(dualToneDarkTheme, R.attr.fillColor);
-        mLightModeBackgroundColor = Utils.getColorAttr(dualToneLightTheme, R.attr.backgroundColor);
-        mLightModeFillColor = Utils.getColorAttr(dualToneLightTheme, R.attr.fillColor);
-
+        setColorsFromContext(context);
         // Init to not dark at all.
         onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
+
         mUserTracker = new CurrentUserTracker(mContext) {
             @Override
             public void onUserSwitched(int newUserId) {
@@ -148,6 +140,21 @@
         updateShowPercent();
     }
 
+    public void setColorsFromContext(Context context) {
+        if (context == null) {
+            return;
+        }
+
+        Context dualToneDarkTheme = new ContextThemeWrapper(context,
+                Utils.getThemeAttr(context, R.attr.darkIconTheme));
+        Context dualToneLightTheme = new ContextThemeWrapper(context,
+                Utils.getThemeAttr(context, R.attr.lightIconTheme));
+        mDarkModeBackgroundColor = Utils.getColorAttr(dualToneDarkTheme, R.attr.backgroundColor);
+        mDarkModeFillColor = Utils.getColorAttr(dualToneDarkTheme, R.attr.fillColor);
+        mLightModeBackgroundColor = Utils.getColorAttr(dualToneLightTheme, R.attr.backgroundColor);
+        mLightModeFillColor = Utils.getColorAttr(dualToneLightTheme, R.attr.fillColor);
+    }
+
     @Override
     public boolean hasOverlappingRendering() {
         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/chooser/ChooserActivity.java b/packages/SystemUI/src/com/android/systemui/chooser/ChooserActivity.java
deleted file mode 100644
index 085ece7..0000000
--- a/packages/SystemUI/src/com/android/systemui/chooser/ChooserActivity.java
+++ /dev/null
@@ -1,41 +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 com.android.systemui.chooser;
-
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.util.Log;
-
-import com.android.systemui.R;
-
-import java.lang.Thread;
-import java.util.ArrayList;
-
-public final class ChooserActivity extends Activity {
-
-    private static final String TAG = "ChooserActivity";
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        ChooserHelper.onChoose(this);
-        finish();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java b/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java
deleted file mode 100644
index ac22568..0000000
--- a/packages/SystemUI/src/com/android/systemui/chooser/ChooserHelper.java
+++ /dev/null
@@ -1,45 +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 com.android.systemui.chooser;
-
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.util.Log;
-
-import com.android.systemui.R;
-
-public class ChooserHelper {
-
-    private static final String TAG = "ChooserHelper";
-
-    static void onChoose(Activity activity) {
-        final Intent thisIntent = activity.getIntent();
-        final Bundle thisExtras = thisIntent.getExtras();
-        final Intent chosenIntent = thisIntent.getParcelableExtra(Intent.EXTRA_INTENT);
-        final Bundle options = thisIntent.getParcelableExtra(ActivityManager.EXTRA_OPTIONS);
-        final IBinder permissionToken =
-                thisExtras.getBinder(ActivityManager.EXTRA_PERMISSION_TOKEN);
-        final boolean ignoreTargetSecurity =
-                thisIntent.getBooleanExtra(ActivityManager.EXTRA_IGNORE_TARGET_SECURITY, false);
-        final int userId = thisIntent.getIntExtra(Intent.EXTRA_USER_ID, -1);
-        activity.startActivityAsCaller(
-                chosenIntent, options, permissionToken, ignoreTargetSecurity, userId);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 77768b1..4d7333b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -97,7 +97,6 @@
         mIconManager.setTint(fillColor);
 
         BatteryMeterView battery = findViewById(R.id.battery);
-        battery.setFillColor(Color.WHITE);
         battery.setForceShowPercent(true);
 
         mActivityStarter = Dependency.get(ActivityStarter.class);
@@ -216,6 +215,11 @@
         //host.setHeaderView(mExpandIndicator);
         mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
         mHeaderQsPanel.setHost(host, null /* No customization in header */);
+
+        // Use SystemUI context to get battery meter colors, and let it use the default tint (white)
+        BatteryMeterView battery = findViewById(R.id.battery);
+        battery.setColorsFromContext(mHost.getContext());
+        battery.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
     }
 
     public void setCallback(Callback qsPanelCallback) {
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 907af69..11d20b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -56,10 +56,15 @@
     public static final long ANIMATION_DELAY_ICON_FADE_IN = ANIMATION_DURATION -
             CollapsedStatusBarFragment.FADE_IN_DURATION - CollapsedStatusBarFragment.FADE_IN_DELAY
             - 16;
+    private static final long LAUNCH_TIMEOUT = 500;
     private final NotificationPanelView mNotificationPanel;
     private final NotificationListContainer mNotificationContainer;
     private final StatusBarWindowView mStatusBarWindow;
-    private final StatusBar mStatusBar;
+    private StatusBar mStatusBar;
+    private final Runnable mTimeoutRunnable = () -> {
+        setAnimationPending(false);
+        mStatusBar.collapsePanel(true /* animate */);
+    };
     private boolean mAnimationPending;
 
     public ActivityLaunchAnimator(StatusBarWindowView statusBarWindow,
@@ -92,6 +97,11 @@
     private void setAnimationPending(boolean pending) {
         mAnimationPending = pending;
         mStatusBarWindow.setExpandAnimationPending(pending);
+        if (pending) {
+            mStatusBarWindow.postDelayed(mTimeoutRunnable, LAUNCH_TIMEOUT);
+        } else {
+            mStatusBarWindow.removeCallbacks(mTimeoutRunnable);
+        }
     }
 
     class AnimationRunner extends IRemoteAnimationRunner.Stub {
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 b519824..426268b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2656,6 +2656,10 @@
         if (mStatusBarView != null) {
             dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
         }
+        pw.println("  StatusBarWindowView: ");
+        if (mStatusBarWindow != null) {
+            mStatusBarWindow.dump(fd, pw, args);
+        }
 
         pw.println("  mMediaManager: ");
         if (mMediaManager != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index e32914f..a79a41b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -62,6 +62,9 @@
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 
 public class StatusBarWindowView extends FrameLayout {
     public static final String TAG = "StatusBarWindowView";
@@ -398,6 +401,13 @@
         mExpandAnimationPending = pending;
     }
 
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.print("  mExpandAnimationPending="); pw.println(mExpandAnimationPending);
+        pw.print("  mExpandAnimationRunning="); pw.println(mExpandAnimationRunning);
+        pw.print("  mTouchCancelled="); pw.println(mTouchCancelled);
+        pw.print("  mTouchActive="); pw.println(mTouchActive);
+    }
+
     public class LayoutParams extends FrameLayout.LayoutParams {
 
         public boolean ignoreRightInset;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/chooser/ChooserHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/chooser/ChooserHelperTest.java
deleted file mode 100644
index 8e0426a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/chooser/ChooserHelperTest.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.chooser;
-
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.content.Intent;
-import android.os.Binder;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
-
-import com.android.systemui.chooser.ChooserHelper;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyFloat;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ChooserHelperTest extends SysuiTestCase {
-
-    @Test
-    public void testOnChoose_CallsStartActivityAsCallerWithToken() {
-        final Intent intent = new Intent();
-        final Binder token = new Binder();
-        intent.putExtra(ActivityManager.EXTRA_PERMISSION_TOKEN, token);
-
-        final Activity mockActivity = mock(Activity.class);
-        when(mockActivity.getIntent()).thenReturn(intent);
-
-        ChooserHelper.onChoose(mockActivity);
-        verify(mockActivity, times(1)).startActivityAsCaller(
-                any(), any(), eq(token), anyBoolean(), anyInt());
-    }
-}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index ba9883b..8265262 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -835,6 +835,9 @@
                         case "level":
                             mHealthInfo.batteryLevel = Integer.parseInt(value);
                             break;
+                        case "counter":
+                            mHealthInfo.batteryChargeCounter = Integer.parseInt(value);
+                            break;
                         case "temp":
                             mHealthInfo.batteryTemperature = Integer.parseInt(value);
                             break;
@@ -1164,6 +1167,20 @@
         }
 
         @Override
+        public int getBatteryChargeCounter() {
+            synchronized (mLock) {
+                return mHealthInfo.batteryChargeCounter;
+            }
+        }
+
+        @Override
+        public int getBatteryFullCharge() {
+            synchronized (mLock) {
+                return mHealthInfo.batteryFullCharge;
+            }
+        }
+
+        @Override
         public boolean getBatteryLevelLow() {
             synchronized (mLock) {
                 return mBatteryLevelLow;
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 732ac66..219facd 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -34,6 +34,7 @@
 2731 power_soft_sleep_requested (savedwaketimems|2)
 # Power save state has changed. See BatterySaverController.java for the details.
 2739 battery_saver_mode (prevOffOrOn|1|5),(nowOffOrOn|1|5),(interactive|1|5),(features|3|5)
+27390 battery_saving_stats (batterySaver|1|5),(interactive|1|5),(doze|1|5),(delta_duration|2|3),(delta_battery_drain|1|6),(total_duration|2|3),(total_battery_drain|1|6)
 
 #
 # Leave IDs through 2740 for more power logs (2730 used by battery_discharge above)
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 3d7408e..2869114 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -372,15 +372,19 @@
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+            long totalSize = 0;
             pw.println("Pinned Files:");
             synchronized(this) {
                 for (int i = 0; i < mPinnedFiles.size(); i++) {
                     pw.println(mPinnedFiles.get(i).mFilename);
+                    totalSize += mPinnedFiles.get(i).mLength;
                 }
                 for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
                     pw.println(mPinnedCameraFiles.get(i).mFilename);
+                    totalSize += mPinnedCameraFiles.get(i).mLength;
                 }
             }
+            pw.println("Total size: " + totalSize);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index fffb4b5..f6f7d4b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -26,7 +26,6 @@
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 import static android.Manifest.permission.READ_FRAME_BUFFER;
 import static android.Manifest.permission.REMOVE_TASKS;
-import static android.Manifest.permission.START_ACTIVITY_AS_CALLER;
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
 import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
@@ -571,23 +570,6 @@
     // could take much longer than usual.
     static final int PROC_START_TIMEOUT_WITH_WRAPPER = 1200*1000;
 
-    // Permission tokens are used to temporarily granted a trusted app the ability to call
-    // #startActivityAsCaller.  A client is expected to dump its token after this time has elapsed,
-    // showing any appropriate error messages to the user.
-    private static final long START_AS_CALLER_TOKEN_TIMEOUT =
-            10 * DateUtils.MINUTE_IN_MILLIS;
-
-    // How long before the service actually expires a token.  This is slightly longer than
-    // START_AS_CALLER_TOKEN_TIMEOUT, to provide a buffer so clients will rarely encounter the
-    // expiration exception.
-    private static final long START_AS_CALLER_TOKEN_TIMEOUT_IMPL =
-            START_AS_CALLER_TOKEN_TIMEOUT + 2*1000;
-
-    // How long the service will remember expired tokens, for the purpose of providing error
-    // messaging when a client uses an expired token.
-    private static final long START_AS_CALLER_TOKEN_EXPIRED_TIMEOUT =
-            START_AS_CALLER_TOKEN_TIMEOUT_IMPL + 20 * DateUtils.MINUTE_IN_MILLIS;
-
     // How long we allow a receiver to run before giving up on it.
     static final int BROADCAST_FG_TIMEOUT = 10*1000;
     static final int BROADCAST_BG_TIMEOUT = 60*1000;
@@ -696,13 +678,6 @@
 
     final ArrayList<ActiveInstrumentation> mActiveInstrumentation = new ArrayList<>();
 
-    // Activity tokens of system activities that are delegating their call to
-    // #startActivityByCaller, keyed by the permissionToken granted to the delegate.
-    final HashMap<IBinder, IBinder> mStartActivitySources = new HashMap<>();
-
-    // Permission tokens that have expired, but we remember for error reporting.
-    final ArrayList<IBinder> mExpiredStartAsCallerTokens = new ArrayList<>();
-
     public final IntentFirewall mIntentFirewall;
 
     // Whether we should show our dialogs (ANR, crash, etc) or just perform their
@@ -1881,8 +1856,6 @@
     static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
     static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
     static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
-    static final int EXPIRE_START_AS_CALLER_TOKEN_MSG = 75;
-    static final int FORGET_START_AS_CALLER_TOKEN_MSG = 76;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -2547,19 +2520,6 @@
                     }
                 }
             } break;
-            case EXPIRE_START_AS_CALLER_TOKEN_MSG: {
-                synchronized (ActivityManagerService.this) {
-                    final IBinder permissionToken = (IBinder)msg.obj;
-                    mStartActivitySources.remove(permissionToken);
-                    mExpiredStartAsCallerTokens.add(permissionToken);
-                }
-            } break;
-            case FORGET_START_AS_CALLER_TOKEN_MSG: {
-                synchronized (ActivityManagerService.this) {
-                    final IBinder permissionToken = (IBinder)msg.obj;
-                    mExpiredStartAsCallerTokens.remove(permissionToken);
-                }
-            } break;
             }
         }
     };
@@ -4845,54 +4805,16 @@
 
     }
 
-    /**
-     * Only callable from the system. This token grants a temporary permission to call
-     * #startActivityAsCallerWithToken. The token will time out after
-     * START_AS_CALLER_TOKEN_TIMEOUT if it is not used.
-     *
-     * @param delegatorToken The Binder token referencing the system Activity that wants to delegate
-     *        the #startActivityAsCaller to another app. The "caller" will be the caller of this
-     *        activity's token, not the delegate's caller (which is probably the delegator itself).
-     *
-     * @return Returns a token that can be given to a "delegate" app that may call
-     *         #startActivityAsCaller
-     */
     @Override
-    public IBinder requestStartActivityPermissionToken(IBinder delegatorToken) {
-        int callingUid = Binder.getCallingUid();
-        if (UserHandle.getAppId(callingUid) != SYSTEM_UID) {
-            throw new SecurityException("Only the system process can request a permission token, " +
-                    "received request from uid: " + callingUid);
-        }
-        IBinder permissionToken = new Binder();
-        synchronized (this) {
-            mStartActivitySources.put(permissionToken, delegatorToken);
-        }
+    public final int startActivityAsCaller(IApplicationThread caller, String callingPackage,
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, boolean ignoreTargetSecurity,
+            int userId) {
 
-        Message expireMsg = mHandler.obtainMessage(EXPIRE_START_AS_CALLER_TOKEN_MSG,
-                permissionToken);
-        mHandler.sendMessageDelayed(expireMsg, START_AS_CALLER_TOKEN_TIMEOUT_IMPL);
-
-        Message forgetMsg = mHandler.obtainMessage(FORGET_START_AS_CALLER_TOKEN_MSG,
-                permissionToken);
-        mHandler.sendMessageDelayed(forgetMsg, START_AS_CALLER_TOKEN_EXPIRED_TIMEOUT);
-
-        return permissionToken;
-    }
-
-    @Override
-    public final int startActivityAsCaller(IApplicationThread caller,
-            String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
-            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
-            Bundle bOptions, IBinder permissionToken, boolean ignoreTargetSecurity, int userId) {
         // This is very dangerous -- it allows you to perform a start activity (including
-        // permission grants) as any app that may launch one of your own activities.  So we only
-        // allow this in two cases:
-        // 1)  The caller is an activity that is part of the core framework, and then only when it
-        //     is running as the system.
-        // 2)  The caller provides a valid permissionToken.  Permission tokens are one-time use and
-        //     can only be requested by a system activity, which may then delegate this call to
-        //     another app.
+        // permission grants) as any app that may launch one of your own activities.  So
+        // we will only allow this to be done from activities that are part of the core framework,
+        // and then only when they are running as the system.
         final ActivityRecord sourceRecord;
         final int targetUid;
         final String targetPackage;
@@ -4900,47 +4822,17 @@
             if (resultTo == null) {
                 throw new SecurityException("Must be called from an activity");
             }
-
-            final IBinder sourceToken;
-            if (permissionToken != null) {
-                // To even attempt to use a permissionToken, an app must also have this signature
-                // permission.
-                enforceCallingPermission(android.Manifest.permission.START_ACTIVITY_AS_CALLER,
-                        "startActivityAsCaller");
-                // If called with a permissionToken, we want the sourceRecord from the delegator
-                // activity that requested this token.
-                sourceToken =
-                        mStartActivitySources.remove(permissionToken);
-                if (sourceToken == null) {
-                    // Invalid permissionToken, check if it recently expired.
-                    if (mExpiredStartAsCallerTokens.contains(permissionToken)) {
-                        throw new SecurityException("Called with expired permission token: "
-                                + permissionToken);
-                    } else {
-                        throw new SecurityException("Called with invalid permission token: "
-                                + permissionToken);
-                    }
-                }
-            } else {
-                // This method was called directly by the source.
-                sourceToken = resultTo;
-            }
-
-            sourceRecord = mStackSupervisor.isInAnyStackLocked(sourceToken);
+            sourceRecord = mStackSupervisor.isInAnyStackLocked(resultTo);
             if (sourceRecord == null) {
-                throw new SecurityException("Called with bad activity token: " + sourceToken);
+                throw new SecurityException("Called with bad activity token: " + resultTo);
+            }
+            if (!sourceRecord.info.packageName.equals("android")) {
+                throw new SecurityException(
+                        "Must be called from an activity that is declared in the android package");
             }
             if (sourceRecord.app == null) {
                 throw new SecurityException("Called without a process attached to activity");
             }
-
-            // Whether called directly or from a delegate, the source activity must be from the
-            // android package.
-            if (!sourceRecord.info.packageName.equals("android")) {
-                throw new SecurityException("Must be called from an activity that is " +
-                        "declared in the android package");
-            }
-
             if (UserHandle.getAppId(sourceRecord.app.uid) != SYSTEM_UID) {
                 // This is still okay, as long as this activity is running under the
                 // uid of the original calling activity.
@@ -4951,7 +4843,6 @@
                                     + sourceRecord.launchedFromUid);
                 }
             }
-
             if (ignoreTargetSecurity) {
                 if (intent.getComponent() == null) {
                     throw new SecurityException(
@@ -14853,6 +14744,8 @@
             mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
 
             BinderInternal.nSetBinderProxyCountEnabled(true);
+            //STOPSHIP: Temporary BinderProxy Threshold for b/71353150
+            BinderInternal.nSetBinderProxyCountWatermarks(1500, 1200);
             BinderInternal.setBinderProxyCountCallback(
                     new BinderInternal.BinderProxyLimitListener() {
                         @Override
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ec8cf91..fd3f8ec 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -453,6 +453,9 @@
         mStackId = stackId;
         mCurrentUser = mService.mUserController.getCurrentUserId();
         mTmpRect2.setEmpty();
+        // Set display id before setting activity and window type to make sure it won't affect
+        // stacks on a wrong display.
+        mDisplayId = display.mDisplayId;
         setActivityType(activityType);
         setWindowingMode(windowingMode);
         mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index f0b27a7..c188ccb 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -70,55 +70,61 @@
 
     void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
             ComponentName recentsComponent, int recentsUid) {
+        mWindowManager.deferSurfaceLayout();
+        try {
+            // Cancel the previous recents animation if necessary
+            mWindowManager.cancelRecentsAnimation();
 
-        // Cancel the previous recents animation if necessary
-        mWindowManager.cancelRecentsAnimation();
+            final boolean hasExistingHomeActivity = mStackSupervisor.getHomeActivity() != null;
+            if (!hasExistingHomeActivity) {
+                // No home activity
+                final ActivityOptions opts = ActivityOptions.makeBasic();
+                opts.setLaunchActivityType(ACTIVITY_TYPE_HOME);
+                opts.setAvoidMoveToFront();
+                intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION);
 
-        final boolean hasExistingHomeActivity = mStackSupervisor.getHomeActivity() != null;
-        if (!hasExistingHomeActivity) {
-            // No home activity
-            final ActivityOptions opts = ActivityOptions.makeBasic();
-            opts.setLaunchActivityType(ACTIVITY_TYPE_HOME);
-            opts.setAvoidMoveToFront();
-            intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION);
+                mActivityStartController
+                        .obtainStarter(intent, "startRecentsActivity_noHomeActivity")
+                        .setCallingUid(recentsUid)
+                        .setCallingPackage(recentsComponent.getPackageName())
+                        .setActivityOptions(SafeActivityOptions.fromBundle(opts.toBundle()))
+                        .setMayWait(mUserController.getCurrentUserId())
+                        .execute();
+                mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
 
-            mActivityStartController.obtainStarter(intent, "startRecentsActivity_noHomeActivity")
-                    .setCallingUid(recentsUid)
-                    .setCallingPackage(recentsComponent.getPackageName())
-                    .setActivityOptions(SafeActivityOptions.fromBundle(opts.toBundle()))
-                    .setMayWait(mUserController.getCurrentUserId())
-                    .execute();
-            mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
+                // TODO: Maybe wait for app to draw in this particular case?
+            }
 
-            // TODO: Maybe wait for app to draw in this particular case?
+            final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
+            final ActivityDisplay display = homeActivity.getDisplay();
+
+            // Save the initial position of the home activity stack to be restored to after the
+            // animation completes
+            mRestoreHomeBehindStack = hasExistingHomeActivity
+                    ? display.getStackAboveHome()
+                    : null;
+
+            // Move the home activity into place for the animation
+            display.moveHomeStackBehindBottomMostVisibleStack();
+
+            // Mark the home activity as launch-behind to bump its visibility for the
+            // duration of the gesture that is driven by the recents component
+            homeActivity.mLaunchTaskBehind = true;
+
+            // Fetch all the surface controls and pass them to the client to get the animation
+            // started
+            mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this,
+                    display.mDisplayId);
+
+            // If we updated the launch-behind state, update the visibility of the activities after
+            // we fetch the visible tasks to be controlled by the animation
+            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
+
+            // Post a timeout for the animation
+            mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
+        } finally {
+            mWindowManager.continueSurfaceLayout();
         }
-
-        final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
-        final ActivityDisplay display = homeActivity.getDisplay();
-
-        // Save the initial position of the home activity stack to be restored to after the
-        // animation completes
-        mRestoreHomeBehindStack = hasExistingHomeActivity
-                ? display.getStackAboveHome()
-                : null;
-
-        // Move the home activity into place for the animation
-        display.moveHomeStackBehindBottomMostVisibleStack();
-
-        // Mark the home activity as launch-behind to bump its visibility for the
-        // duration of the gesture that is driven by the recents component
-        homeActivity.mLaunchTaskBehind = true;
-
-        // Fetch all the surface controls and pass them to the client to get the animation
-        // started
-        mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this, display.mDisplayId);
-
-        // If we updated the launch-behind state, update the visibility of the activities after we
-        // fetch the visible tasks to be controlled by the animation
-        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
-
-        // Post a timeout for the animation
-        mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
     }
 
     @Override
@@ -128,35 +134,40 @@
             if (mWindowManager.getRecentsAnimationController() == null) return;
 
             mWindowManager.inSurfaceTransaction(() -> {
-                mWindowManager.cleanupRecentsAnimation();
+                mWindowManager.deferSurfaceLayout();
+                try {
+                    mWindowManager.cleanupRecentsAnimation();
 
-                // Move the home stack to the front
-                final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
-                if (homeActivity == null) {
-                    return;
+                    // Move the home stack to the front
+                    final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
+                    if (homeActivity == null) {
+                        return;
+                    }
+
+                    // Restore the launched-behind state
+                    homeActivity.mLaunchTaskBehind = false;
+
+                    if (moveHomeToTop) {
+                        // Bring the home stack to the front
+                        final ActivityStack homeStack = homeActivity.getStack();
+                        homeStack.mNoAnimActivities.add(homeActivity);
+                        homeStack.moveToFront("RecentsAnimation.onAnimationFinished()");
+                    } else {
+                        // Restore the home stack to its previous position
+                        final ActivityDisplay display = homeActivity.getDisplay();
+                        display.moveHomeStackBehindStack(mRestoreHomeBehindStack);
+                    }
+
+                    mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
+                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
+                    mStackSupervisor.resumeFocusedStackTopActivityLocked();
+
+                    // No reason to wait for the pausing activity in this case, as the hiding of
+                    // surfaces needs to be done immediately.
+                    mWindowManager.executeAppTransition();
+                } finally {
+                    mWindowManager.continueSurfaceLayout();
                 }
-
-                // Restore the launched-behind state
-                homeActivity.mLaunchTaskBehind = false;
-
-                if (moveHomeToTop) {
-                    // Bring the home stack to the front
-                    final ActivityStack homeStack = homeActivity.getStack();
-                    homeStack.mNoAnimActivities.add(homeActivity);
-                    homeStack.moveToFront("RecentsAnimation.onAnimationFinished()");
-                } else {
-                    // Restore the home stack to its previous position
-                    final ActivityDisplay display = homeActivity.getDisplay();
-                    display.moveHomeStackBehindStack(mRestoreHomeBehindStack);
-                }
-
-                mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
-                mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
-                mStackSupervisor.resumeFocusedStackTopActivityLocked();
-
-                // No reason to wait for the pausing activity in this case, as the hiding of
-                // surfaces needs to be done immediately.
-                mWindowManager.executeAppTransition();
             });
         }
     }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f6bcb25..f247de7 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1329,8 +1329,20 @@
     /** @see AudioManager#adjustVolume(int, int) */
     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
             String callingPackage, String caller) {
-        adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
-                caller, Binder.getCallingUid());
+        final IAudioPolicyCallback extVolCtlr;
+        synchronized (mExtVolumeControllerLock) {
+            extVolCtlr = mExtVolumeController;
+        }
+        if (extVolCtlr != null) {
+            try {
+                mExtVolumeController.notifyVolumeAdjust(direction);
+            } catch(RemoteException e) {
+                // nothing we can do about this. Do not log error, too much potential for spam
+            }
+        } else {
+            adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
+                    caller, Binder.getCallingUid());
+        }
     }
 
     private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
@@ -6964,7 +6976,7 @@
     // Audio policy management
     //==========================================================================================
     public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb,
-            boolean hasFocusListener, boolean isFocusPolicy) {
+            boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController) {
         AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback);
 
         if (DEBUG_AP) Log.d(TAG, "registerAudioPolicy for " + pcb.asBinder()
@@ -6987,7 +6999,7 @@
                     return null;
                 }
                 AudioPolicyProxy app = new AudioPolicyProxy(policyConfig, pcb, hasFocusListener,
-                        isFocusPolicy);
+                        isFocusPolicy, isVolumeController);
                 pcb.asBinder().linkToDeath(app, 0/*flags*/);
                 regId = app.getRegistrationId();
                 mAudioPolicies.put(pcb.asBinder(), app);
@@ -7053,6 +7065,23 @@
         return AudioManager.SUCCESS;
     }
 
+    private final Object mExtVolumeControllerLock = new Object();
+    private IAudioPolicyCallback mExtVolumeController;
+    private void setExtVolumeController(IAudioPolicyCallback apc) {
+        if (!mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_handleVolumeKeysInWindowManager)) {
+            Log.e(TAG, "Cannot set external volume controller: device not set for volume keys" +
+                    " handled in PhoneWindowManager");
+            return;
+        }
+        synchronized (mExtVolumeControllerLock) {
+            if (mExtVolumeController != null && !mExtVolumeController.asBinder().pingBinder()) {
+                Log.e(TAG, "Cannot set external volume controller: existing controller");
+            }
+            mExtVolumeController = apc;
+        }
+    }
+
     private void dumpAudioPolicies(PrintWriter pw) {
         pw.println("\nAudio policies:");
         synchronized (mAudioPolicies) {
@@ -7185,8 +7214,9 @@
      */
     public class AudioPolicyProxy extends AudioPolicyConfig implements IBinder.DeathRecipient {
         private static final String TAG = "AudioPolicyProxy";
-        IAudioPolicyCallback mPolicyCallback;
-        boolean mHasFocusListener;
+        final IAudioPolicyCallback mPolicyCallback;
+        final boolean mHasFocusListener;
+        final boolean mIsVolumeController;
         /**
          * Audio focus ducking behavior for an audio policy.
          * This variable reflects the value that was successfully set in
@@ -7198,11 +7228,12 @@
         boolean mIsFocusPolicy = false;
 
         AudioPolicyProxy(AudioPolicyConfig config, IAudioPolicyCallback token,
-                boolean hasFocusListener, boolean isFocusPolicy) {
+                boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController) {
             super(config);
             setRegistration(new String(config.hashCode() + ":ap:" + mAudioPolicyCounter++));
             mPolicyCallback = token;
             mHasFocusListener = hasFocusListener;
+            mIsVolumeController = isVolumeController;
             if (mHasFocusListener) {
                 mMediaFocusControl.addFocusFollower(mPolicyCallback);
                 // can only ever be true if there is a focus listener
@@ -7211,6 +7242,9 @@
                     mMediaFocusControl.setFocusPolicy(mPolicyCallback);
                 }
             }
+            if (mIsVolumeController) {
+                setExtVolumeController(mPolicyCallback);
+            }
             connectMixes();
         }
 
@@ -7220,6 +7254,11 @@
                 release();
                 mAudioPolicies.remove(mPolicyCallback.asBinder());
             }
+            if (mIsVolumeController) {
+                synchronized (mExtVolumeControllerLock) {
+                    mExtVolumeController = null;
+                }
+            }
         }
 
         String getRegistrationId() {
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index b5f94b1..25a2100 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -421,7 +421,7 @@
                 byteToken[i] = token.get(i);
             }
             // Send to Keystore
-            KeyStore.getInstance().addAuthToken(byteToken);
+            KeyStore.getInstance().addAuthToken(byteToken, mCurrentUserId);
         }
         if (client != null && client.onAuthenticated(fingerId, groupId)) {
             removeClient(client);
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index bfc858f..0229a37 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -296,6 +296,7 @@
                 PackageManagerService.reportSettingsProblem(Log.WARN,
                         "Unknown element under <sigs>: "
                                 + parser.getName());
+                XmlUtils.skipCurrentTag(parser);
             }
         }
         return pos;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0f394a4..0502848 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1025,7 +1025,7 @@
             public void run() {
                 // send interaction hint to improve redraw performance
                 mPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);
-                if (isRotationChoiceEnabled()) {
+                if (isRotationChoicePossible(mCurrentAppOrientation)) {
                     final boolean isValid = isValidRotationChoice(mCurrentAppOrientation,
                             mRotation);
                     sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
@@ -7144,7 +7144,7 @@
         mOrientationListener.setCurrentRotation(rotation);
     }
 
-    public boolean isRotationChoiceEnabled() {
+    public boolean isRotationChoicePossible(int orientation) {
         // Rotation choice is only shown when the user is in locked mode.
         if (mUserRotationMode != WindowManagerPolicy.USER_ROTATION_LOCKED) return false;
 
@@ -7184,50 +7184,45 @@
             return false;
         }
 
-        // Rotation isn't forced, enable choice
-        return true;
+        // Ensure that some rotation choice is possible for the given orientation
+        switch (orientation) {
+            case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
+            case ActivityInfo.SCREEN_ORIENTATION_USER:
+            case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
+            case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+            case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+                // NOSENSOR description is ambiguous, in reality WM ignores user choice
+                return true;
+        }
+
+        // Rotation is forced, should be controlled by system
+        return false;
     }
 
     public boolean isValidRotationChoice(int orientation, final int preferredRotation) {
-        // Determine if the given app orientation can be chosen and, if so, if it is compatible
-        // with the provided rotation choice
-
+        // Determine if the given app orientation is compatible with the provided rotation choice
         switch (orientation) {
-            case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
-            case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
-            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
-            case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
-            case ActivityInfo.SCREEN_ORIENTATION_LOCKED:
-                return false; // Forced into a particular rotation, no user choice
-
-            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
-            case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
-            case ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR:
-            case ActivityInfo.SCREEN_ORIENTATION_SENSOR:
-                return false; // Sensor overrides user choice
-
-            case ActivityInfo.SCREEN_ORIENTATION_NOSENSOR:
-                // TODO Can sensor be used to indirectly determine the orientation?
-                return false;
-
-            case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
-                // If the user has locked sensor-based rotation, this behaves the same as landscape
-                return false; // User has locked the rotation, will behave as LANDSCAPE
-            case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
-                // If the user has locked sensor-based rotation, this behaves the same as portrait
-                return false; // User has locked the rotation, will behave as PORTRAIT
-            case ActivityInfo.SCREEN_ORIENTATION_USER:
-                // Works with any rotation except upside down
-                return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
             case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
                 // Works with any of the 4 rotations
                 return preferredRotation >= 0;
 
-            default:
-                // TODO: how to handle SCREEN_ORIENTATION_BEHIND, UNSET?
-                // For UNSPECIFIED use preferred orientation matching SCREEN_ORIENTATION_USER
+            case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+                // It's possible for the user pref to be set at 180 because of FULL_USER. This would
+                // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait
+                // but never to go to 180.
+                return preferredRotation == mPortraitRotation;
+
+            case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+                // Works landscape or seascape
+                return isLandscapeOrSeascape(preferredRotation);
+
+            case ActivityInfo.SCREEN_ORIENTATION_USER:
+            case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
+                // Works with any rotation except upside down
                 return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
         }
+
+        return false;
     }
 
     private boolean isLandscapeOrSeascape(int rotation) {
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
index 941cd44..efcadad 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
@@ -19,6 +19,8 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.security.IKeystoreService;
 import android.util.Slog;
 
 import com.android.internal.policy.IKeyguardService;
@@ -51,11 +53,16 @@
     private final LockPatternUtils mLockPatternUtils;
     private final StateCallback mCallback;
 
+    IKeystoreService mKeystoreService;
+
     public KeyguardStateMonitor(Context context, IKeyguardService service, StateCallback callback) {
         mLockPatternUtils = new LockPatternUtils(context);
         mCurrentUserId = ActivityManager.getCurrentUser();
         mCallback = callback;
 
+        mKeystoreService = IKeystoreService.Stub.asInterface(ServiceManager
+                .getService("android.security.keystore"));
+
         try {
             service.addStateMonitorCallback(this);
         } catch (RemoteException e) {
@@ -86,6 +93,12 @@
     @Override // Binder interface
     public void onShowingStateChanged(boolean showing) {
         mIsShowing = showing;
+
+        if (showing) try {
+            mKeystoreService.lock(mCurrentUserId); // as long as this doesn't recur...
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error locking keystore", e);
+        }
     }
 
     @Override // Binder interface
diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java
index a538967..847c90a 100644
--- a/services/core/java/com/android/server/power/BatterySaverPolicy.java
+++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java
@@ -33,6 +33,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.power.batterysaver.BatterySavingStats;
 import com.android.server.power.batterysaver.CpuFrequencies;
 
 import java.io.PrintWriter;
@@ -498,6 +499,8 @@
             pw.print("  Noninteractive File values:\n");
             dumpMap(pw, "    ", mFilesForNoninteractive);
             pw.println();
+            pw.println();
+            BatterySavingStats.getInstance().dump(pw, "  ");
         }
     }
 
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
index d4627c2..32f38b7 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverController.java
@@ -28,6 +28,7 @@
 import android.content.IntentFilter;
 import android.hardware.power.V1_0.PowerHint;
 import android.net.Uri;
+import android.os.BatteryManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -50,6 +51,9 @@
 import com.android.server.power.BatterySaverPolicy;
 import com.android.server.power.BatterySaverPolicy.BatterySaverPolicyListener;
 import com.android.server.power.PowerManagerService;
+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 java.util.ArrayList;
 
@@ -70,6 +74,8 @@
 
     private final BatterySaverPolicy mBatterySaverPolicy;
 
+    private final BatterySavingStats mBatterySavingStats;
+
     private static final String WARNING_LINK_URL = "http://goto.google.com/extreme-battery-saver";
 
     @GuardedBy("mLock")
@@ -78,6 +84,9 @@
     @GuardedBy("mLock")
     private boolean mEnabled;
 
+    @GuardedBy("mLock")
+    private boolean mIsPluggedIn;
+
     /**
      * Previously enabled or not; only for the event logging. Only use it from
      * {@link #handleBatterySaverStateChanged}.
@@ -104,15 +113,28 @@
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            if (DEBUG) {
+                Slog.d(TAG, "onReceive: " + intent);
+            }
             switch (intent.getAction()) {
                 case Intent.ACTION_SCREEN_ON:
                 case Intent.ACTION_SCREEN_OFF:
                     if (!isEnabled()) {
+                        updateBatterySavingStats();
                         return; // No need to send it if not enabled.
                     }
                     // Don't send the broadcast, because we never did so in this case.
                     mHandler.postStateChanged(/*sendBroadcast=*/ false);
                     break;
+                case Intent.ACTION_BATTERY_CHANGED:
+                    synchronized (mLock) {
+                        mIsPluggedIn = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0);
+                    }
+                    // Fall-through.
+                case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
+                case PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED:
+                    updateBatterySavingStats();
+                    break;
             }
         }
     };
@@ -126,6 +148,7 @@
         mBatterySaverPolicy = policy;
         mBatterySaverPolicy.addListener(this);
         mFileUpdater = new FileUpdater(context);
+        mBatterySavingStats = BatterySavingStats.getInstance();
 
         // Initialize plugins.
         final ArrayList<Plugin> plugins = new ArrayList<>();
@@ -149,6 +172,9 @@
     public void systemReady() {
         final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
+        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+        filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+        filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
         mContext.registerReceiver(mReceiver, filter);
 
         mFileUpdater.systemReady(LocalServices.getService(ActivityManagerInternal.class)
@@ -280,7 +306,6 @@
             enabled = mEnabled;
             mIsInteractive = isInteractive;
 
-
             if (enabled) {
                 fileValues = mBatterySaverPolicy.getFileValues(isInteractive);
             } else {
@@ -293,6 +318,8 @@
             pmi.powerHint(PowerHint.LOW_POWER, enabled ? 1 : 0);
         }
 
+        updateBatterySavingStats();
+
         if (ArrayUtils.isEmpty(fileValues)) {
             mFileUpdater.restoreDefault();
         } else {
@@ -332,7 +359,6 @@
             mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
                     Manifest.permission.DEVICE_POWER);
 
-
             for (LowPowerModeListener listener : listeners) {
                 final PowerSaveState result =
                         mBatterySaverPolicy.getBatterySaverPolicy(
@@ -388,4 +414,28 @@
                     foregroundUser);
         }
     }
+
+    private void updateBatterySavingStats() {
+        final PowerManager pm = getPowerManager();
+        if (pm == null) {
+            Slog.wtf(TAG, "PowerManager not initialized");
+            return;
+        }
+        final boolean isInteractive = pm.isInteractive();
+        final int dozeMode =
+                pm.isDeviceIdleMode() ? DozeState.DEEP
+                        : pm.isLightDeviceIdleMode() ? DozeState.LIGHT
+                        : DozeState.NOT_DOZING;
+
+        synchronized (mLock) {
+            if (mIsPluggedIn) {
+                mBatterySavingStats.startCharging();
+                return;
+            }
+            mBatterySavingStats.transitionState(
+                    mEnabled ? BatterySaverState.ON : BatterySaverState.OFF,
+                    isInteractive ? InteractiveState.INTERACTIVE : InteractiveState.NON_INTERACTIVE,
+                    dozeMode);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
new file mode 100644
index 0000000..df4f8ec
--- /dev/null
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySavingStats.java
@@ -0,0 +1,353 @@
+/*
+ * 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.power.batterysaver;
+
+import android.os.BatteryManagerInternal;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
+import com.android.server.power.BatterySaverPolicy;
+
+import java.io.PrintWriter;
+
+/**
+ * This class keeps track of battery drain rate.
+ *
+ * Test:
+ atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
+ */
+public class BatterySavingStats {
+
+    private static final String TAG = "BatterySavingStats";
+
+    private static final boolean DEBUG = BatterySaverPolicy.DEBUG;
+
+    private final Object mLock = new Object();
+
+    /** Whether battery saver is on or off. */
+    interface BatterySaverState {
+        int OFF = 0;
+        int ON = 1;
+
+        int SHIFT = 0;
+        int BITS = 1;
+        int MASK = (1 << BITS) - 1;
+
+        static int fromIndex(int index) {
+            return (index >> SHIFT) & MASK;
+        }
+    }
+
+    /** Whether the device is interactive (i.e. screen on) or not. */
+    interface InteractiveState {
+        int NON_INTERACTIVE = 0;
+        int INTERACTIVE = 1;
+
+        int SHIFT = BatterySaverState.SHIFT + BatterySaverState.BITS;
+        int BITS = 1;
+        int MASK = (1 << BITS) - 1;
+
+        static int fromIndex(int index) {
+            return (index >> SHIFT) & MASK;
+        }
+    }
+
+    /** Doze mode. */
+    interface DozeState {
+        int NOT_DOZING = 0;
+        int LIGHT = 1;
+        int DEEP = 2;
+
+        int SHIFT = InteractiveState.SHIFT + InteractiveState.BITS;
+        int BITS = 2;
+        int MASK = (1 << BITS) - 1;
+
+        static int fromIndex(int index) {
+            return (index >> SHIFT) & MASK;
+        }
+    }
+
+    /**
+     * Various stats in each state.
+     */
+    static class Stat {
+        public long startTime;
+        public long endTime;
+
+        public int startBatteryLevel;
+        public int endBatteryLevel;
+
+        public long totalTimeMillis;
+        public int totalBatteryDrain;
+
+        public long totalMinutes() {
+            return totalTimeMillis / 60_000;
+        }
+
+        public double drainPerHour() {
+            if (totalTimeMillis == 0) {
+                return 0;
+            }
+            return (double) totalBatteryDrain / (totalTimeMillis / (60.0 * 60 * 1000));
+        }
+
+        @VisibleForTesting
+        String toStringForTest() {
+            return "{" + totalMinutes() + "m," + totalBatteryDrain + ","
+                    + String.format("%.2f", drainPerHour()) + "}";
+        }
+    }
+
+    private static BatterySavingStats sInstance;
+
+    private BatteryManagerInternal mBatteryManagerInternal;
+
+    private static final int STATE_NOT_INITIALIZED = -1;
+    private static final int STATE_CHARGING = -2;
+
+    /**
+     * Current state, one of STATE_* or values returned by {@link #statesToIndex}.
+     */
+    @GuardedBy("mLock")
+    private int mCurrentState = STATE_NOT_INITIALIZED;
+
+    /**
+     * Stats in each state.
+     */
+    @VisibleForTesting
+    @GuardedBy("mLock")
+    final ArrayMap<Integer, Stat> mStats = new ArrayMap<>();
+
+    /**
+     * Don't call it directly -- use {@link #getInstance()}. Not private for testing.
+     */
+    @VisibleForTesting
+    BatterySavingStats() {
+        mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
+    }
+
+    public static synchronized BatterySavingStats getInstance() {
+        if (sInstance == null) {
+            sInstance = new BatterySavingStats();
+        }
+        return sInstance;
+    }
+
+    private BatteryManagerInternal getBatteryManagerInternal() {
+        if (mBatteryManagerInternal == null) {
+            mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
+        }
+        return mBatteryManagerInternal;
+    }
+
+    /**
+     * Takes a state triplet and generates a state index.
+     */
+    @VisibleForTesting
+    static int statesToIndex(
+            int batterySaverState, int interactiveState, int dozeState) {
+        int ret = batterySaverState & BatterySaverState.MASK;
+        ret |= (interactiveState & InteractiveState.MASK) << InteractiveState.SHIFT;
+        ret |= (dozeState & DozeState.MASK) << DozeState.SHIFT;
+        return ret;
+    }
+
+    /**
+     * Takes a state index and returns a string for logging.
+     */
+    @VisibleForTesting
+    static String stateToString(int state) {
+        switch (state) {
+            case STATE_NOT_INITIALIZED:
+                return "NotInitialized";
+            case STATE_CHARGING:
+                return "Charging";
+        }
+        return "BS=" + BatterySaverState.fromIndex(state)
+                + ",I=" + InteractiveState.fromIndex(state)
+                + ",D=" + DozeState.fromIndex(state);
+    }
+
+    /**
+     * @return {@link Stat} fo a given state.
+     */
+    @VisibleForTesting
+    Stat getStat(int stateIndex) {
+        synchronized (mLock) {
+            Stat stat = mStats.get(stateIndex);
+            if (stat == null) {
+                stat = new Stat();
+                mStats.put(stateIndex, stat);
+            }
+            return stat;
+        }
+    }
+
+    /**
+     * @return {@link Stat} fo a given state triplet.
+     */
+    private Stat getStat(int batterySaverState, int interactiveState, int dozeState) {
+        return getStat(statesToIndex(batterySaverState, interactiveState, dozeState));
+    }
+
+    long injectCurrentTime() {
+        return SystemClock.elapsedRealtime();
+    }
+
+    int injectBatteryLevel() {
+        final BatteryManagerInternal bmi = getBatteryManagerInternal();
+        if (bmi == null) {
+            Slog.wtf(TAG, "BatteryManagerInternal not initialized");
+            return 0;
+        }
+        return bmi.getBatteryChargeCounter();
+    }
+
+    /**
+     * Called from the outside whenever any of the states changes, when the device is not plugged
+     * in.
+     */
+    public void transitionState(int batterySaverState, int interactiveState, int dozeState) {
+        synchronized (mLock) {
+
+            final int newState = statesToIndex(
+                    batterySaverState, interactiveState, dozeState);
+            if (mCurrentState == newState) {
+                return;
+            }
+
+            endLastStateLocked();
+            startNewStateLocked(newState);
+        }
+    }
+
+    /**
+     * Called from the outside when the device is plugged in.
+     */
+    public void startCharging() {
+        synchronized (mLock) {
+            if (mCurrentState < 0) {
+                return;
+            }
+
+            endLastStateLocked();
+            startNewStateLocked(STATE_CHARGING);
+        }
+    }
+
+    private void endLastStateLocked() {
+        if (mCurrentState < 0) {
+            return;
+        }
+        final Stat stat = getStat(mCurrentState);
+
+        stat.endBatteryLevel = injectBatteryLevel();
+        stat.endTime = injectCurrentTime();
+
+        final long deltaTime = stat.endTime - stat.startTime;
+        final int deltaDrain = stat.startBatteryLevel - stat.endBatteryLevel;
+
+        stat.totalTimeMillis += deltaTime;
+        stat.totalBatteryDrain += deltaDrain;
+
+        if (DEBUG) {
+            Slog.d(TAG, "State summary: " + stateToString(mCurrentState)
+                    + ": " + (deltaTime / 1_000) + "s "
+                    + "Start level: " + stat.startBatteryLevel + "uA "
+                    + "End level: " + stat.endBatteryLevel + "uA "
+                    + deltaDrain + "uA");
+        }
+        EventLogTags.writeBatterySavingStats(
+                BatterySaverState.fromIndex(mCurrentState),
+                InteractiveState.fromIndex(mCurrentState),
+                DozeState.fromIndex(mCurrentState),
+                deltaTime,
+                deltaDrain,
+                stat.totalTimeMillis,
+                stat.totalBatteryDrain);
+    }
+
+    private void startNewStateLocked(int newState) {
+        if (DEBUG) {
+            Slog.d(TAG, "New state: " + stateToString(newState));
+        }
+        mCurrentState = newState;
+
+        if (mCurrentState < 0) {
+            return;
+        }
+
+        final Stat stat = getStat(mCurrentState);
+        stat.startBatteryLevel = injectBatteryLevel();
+        stat.startTime = injectCurrentTime();
+        stat.endTime = 0;
+    }
+
+    public void dump(PrintWriter pw, String indent) {
+        synchronized (mLock) {
+            pw.print(indent);
+            pw.println("Battery Saving Stats:");
+
+            indent = indent + "  ";
+
+            pw.print(indent);
+            pw.println("Battery Saver:       Off                                 On");
+            dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr",
+                    DozeState.NOT_DOZING, "NonDoze");
+            dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, "   Intr",
+                    DozeState.NOT_DOZING, "       ");
+
+            dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr",
+                    DozeState.DEEP, "Deep   ");
+            dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, "   Intr",
+                    DozeState.DEEP, "       ");
+
+            dumpLineLocked(pw, indent, InteractiveState.NON_INTERACTIVE, "NonIntr",
+                    DozeState.LIGHT, "Light  ");
+            dumpLineLocked(pw, indent, InteractiveState.INTERACTIVE, "   Intr",
+                    DozeState.LIGHT, "       ");
+
+            pw.println();
+        }
+    }
+
+    private void dumpLineLocked(PrintWriter pw, String indent,
+            int interactiveState, String interactiveLabel,
+            int dozeState, String dozeLabel) {
+        pw.print(indent);
+        pw.print(dozeLabel);
+        pw.print(" ");
+        pw.print(interactiveLabel);
+        pw.print(": ");
+
+        final Stat offStat = getStat(BatterySaverState.OFF, interactiveState, dozeState);
+        final Stat onStat = getStat(BatterySaverState.ON, interactiveState, dozeState);
+
+        pw.println(String.format("%6dm %6dmA %8.1fmA/h      %6dm %6dmA %8.1fmA/h",
+                offStat.totalMinutes(),
+                offStat.totalBatteryDrain / 1000,
+                offStat.drainPerHour() / 1000.0,
+                onStat.totalMinutes(),
+                onStat.totalBatteryDrain / 1000,
+                onStat.drainPerHour() / 1000.0));
+    }
+}
+
diff --git a/services/core/java/com/android/server/slice/PinnedSliceState.java b/services/core/java/com/android/server/slice/PinnedSliceState.java
index 5811714..192fd63 100644
--- a/services/core/java/com/android/server/slice/PinnedSliceState.java
+++ b/services/core/java/com/android/server/slice/PinnedSliceState.java
@@ -166,6 +166,7 @@
     ContentProviderClient getClient() {
         ContentProviderClient client =
                 mService.getContext().getContentResolver().acquireContentProviderClient(mUri);
+        if (client == null) return null;
         client.setDetectNotResponding(SLICE_TIMEOUT);
         return client;
     }
@@ -181,6 +182,7 @@
             }
             if (!isPinned()) {
                 // All the listeners died, remove from pinned state.
+                mService.unlisten(mUri);
                 mService.removePinnedSlice(mUri);
             }
         }
@@ -210,6 +212,7 @@
             }
             if (!isPinned()) {
                 // All the listeners died, remove from pinned state.
+                mService.unlisten(mUri);
                 mService.removePinnedSlice(mUri);
             }
         }
@@ -217,6 +220,7 @@
 
     private Slice doBind(String overridePkg) {
         try (ContentProviderClient client = getClient()) {
+            if (client == null) return null;
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri);
             extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
@@ -237,6 +241,7 @@
 
     private void handleSendPinned() {
         try (ContentProviderClient client = getClient()) {
+            if (client == null) return;
             Bundle b = new Bundle();
             b.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri);
             try {
@@ -249,6 +254,7 @@
 
     private void handleSendUnpinned() {
         try (ContentProviderClient client = getClient()) {
+            if (client == null) return;
             Bundle b = new Bundle();
             b.putParcelable(SliceProvider.EXTRA_BIND_URI, mUri);
             try {
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 9fe16ae..b435605 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -17,34 +17,115 @@
 package com.android.server.wm;
 
 import android.util.ArrayMap;
-import android.util.Slog;
 import android.view.SurfaceControl;
 import android.graphics.Rect;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
  * black layers of varying opacity at various Z-levels which create the effect of a Dim.
  */
 class Dimmer {
     private static final String TAG = "WindowManager";
+    private static final int DEFAULT_DIM_ANIM_DURATION = 200;
 
-    private class DimState {
-        SurfaceControl mSurfaceControl;
+    private class DimAnimatable implements SurfaceAnimator.Animatable {
+        private final SurfaceControl mDimLayer;
+
+        private DimAnimatable(SurfaceControl dimLayer) {
+            mDimLayer = dimLayer;
+        }
+
+        @Override
+        public SurfaceControl.Transaction getPendingTransaction() {
+            return mHost.getPendingTransaction();
+        }
+
+        @Override
+        public void commitPendingTransaction() {
+            mHost.commitPendingTransaction();
+        }
+
+        @Override
+        public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
+        }
+
+        @Override
+        public void onAnimationLeashDestroyed(SurfaceControl.Transaction t) {
+        }
+
+        @Override
+        public void destroyAfterPendingTransaction(SurfaceControl surface) {
+            mHost.destroyAfterPendingTransaction(surface);
+        }
+
+        @Override
+        public SurfaceControl.Builder makeAnimationLeash() {
+            return mHost.makeAnimationLeash();
+        }
+
+        @Override
+        public SurfaceControl getAnimationLeashParent() {
+            return mHost.getSurfaceControl();
+        }
+
+        @Override
+        public SurfaceControl getSurfaceControl() {
+            return mDimLayer;
+        }
+
+        @Override
+        public SurfaceControl getParentSurfaceControl() {
+            return mHost.getSurfaceControl();
+        }
+
+        @Override
+        public int getSurfaceWidth() {
+            // This will determine the size of the leash created. This should be the size of the
+            // host and not the dim layer since the dim layer may get bigger during animation. If
+            // that occurs, the leash size cannot change so we need to ensure the leash is big
+            // enough that the dim layer can grow.
+            // This works because the mHost will be a Task which has the display bounds.
+            return mHost.getSurfaceWidth();
+        }
+
+        @Override
+        public int getSurfaceHeight() {
+            // See getSurfaceWidth() above for explanation.
+            return mHost.getSurfaceHeight();
+        }
+    }
+
+    @VisibleForTesting
+    class DimState {
+        /**
+         * The layer where property changes should be invoked on.
+         */
+        SurfaceControl mDimLayer;
         boolean mDimming;
+        boolean isVisible;
+        SurfaceAnimator mSurfaceAnimator;
 
         /**
-         * Used for Dims not assosciated with a WindowContainer. See {@link Dimmer#dimAbove} for
+         * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
          * details on Dim lifecycle.
          */
         boolean mDontReset;
 
-        DimState(SurfaceControl ctl) {
-            mSurfaceControl = ctl;
+        DimState(SurfaceControl dimLayer) {
+            mDimLayer = dimLayer;
             mDimming = true;
+            mSurfaceAnimator = new SurfaceAnimator(new DimAnimatable(dimLayer), () -> {
+                if (!mDimming) {
+                    mDimLayer.destroy();
+                }
+            }, mHost.mService.mAnimator::addAfterPrepareSurfacesRunnable, mHost.mService);
         }
-    };
+    }
 
-    private ArrayMap<WindowContainer, DimState> mDimLayerUsers = new ArrayMap<>();
+    @VisibleForTesting
+    ArrayMap<WindowContainer, DimState> mDimLayerUsers = new ArrayMap<>();
 
     /**
      * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
@@ -56,19 +137,18 @@
         mHost = host;
     }
 
-    SurfaceControl makeDimLayer() {
-        final SurfaceControl control = mHost.makeChildSurface(null)
+    private SurfaceControl makeDimLayer() {
+        return mHost.makeChildSurface(null)
                 .setParent(mHost.getSurfaceControl())
                 .setColorLayer(true)
                 .setName("Dim Layer for - " + mHost.getName())
                 .build();
-        return control;
     }
 
     /**
      * Retreive the DimState for a given child of the host.
      */
-    DimState getDimState(WindowContainer container) {
+    private DimState getDimState(WindowContainer container) {
         DimState state = mDimLayerUsers.get(container);
         if (state == null) {
             final SurfaceControl ctl = makeDimLayer();
@@ -88,14 +168,12 @@
     private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
             float alpha) {
         final DimState d = getDimState(container);
-        t.show(d.mSurfaceControl);
         if (container != null) {
-            t.setRelativeLayer(d.mSurfaceControl,
-                    container.getSurfaceControl(), relativeLayer);
+            t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
         } else {
-            t.setLayer(d.mSurfaceControl, Integer.MAX_VALUE);
+            t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
         }
-        t.setAlpha(d.mSurfaceControl, alpha);
+        t.setAlpha(d.mDimLayer, alpha);
 
         d.mDimming = true;
     }
@@ -107,16 +185,18 @@
      */
     void stopDim(SurfaceControl.Transaction t) {
         DimState d = getDimState(null);
-        t.hide(d.mSurfaceControl);
+        t.hide(d.mDimLayer);
+        d.isVisible = false;
         d.mDontReset = false;
     }
+
     /**
      * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
      * remove this effect. If the Dim can be assosciated with a particular child of the host
      * consider using the other variant of dimAbove which ties the Dim lifetime to the child
      * lifetime more explicitly.
      *
-     * @param t A transaction in which to apply the Dim.
+     * @param t     A transaction in which to apply the Dim.
      * @param alpha The alpha at which to Dim.
      */
     void dimAbove(SurfaceControl.Transaction t, float alpha) {
@@ -128,9 +208,9 @@
      * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
      * and the child should call dimAbove again to request the Dim to continue.
      *
-     * @param t A transaction in which to apply the Dim.
+     * @param t         A transaction in which to apply the Dim.
      * @param container The container which to dim above. Should be a child of our host.
-     * @param alpha The alpha at which to Dim.
+     * @param alpha     The alpha at which to Dim.
      */
     void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
         dim(t, container, 1, alpha);
@@ -139,9 +219,9 @@
     /**
      * Like {@link #dimAbove} but places the dim below the given container.
      *
-     * @param t A transaction in which to apply the Dim.
+     * @param t         A transaction in which to apply the Dim.
      * @param container The container which to dim below. Should be a child of our host.
-     * @param alpha The alpha at which to Dim.
+     * @param alpha     The alpha at which to Dim.
      */
 
     void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
@@ -159,7 +239,7 @@
     void resetDimStates() {
         for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
             final DimState state = mDimLayerUsers.valueAt(i);
-            if (state.mDontReset == false) {
+            if (!state.mDontReset) {
                 state.mDimming = false;
             }
         }
@@ -169,7 +249,7 @@
      * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
      * described in {@link #resetDimStates}.
      *
-     * @param t A transaction in which to update the dims.
+     * @param t      A transaction in which to update the dims.
      * @param bounds The bounds at which to dim.
      * @return true if any Dims were updated.
      */
@@ -177,19 +257,80 @@
         boolean didSomething = false;
         for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
             DimState state = mDimLayerUsers.valueAt(i);
+            WindowContainer container = mDimLayerUsers.keyAt(i);
+
             // TODO: We want to animate the addition and removal of Dim's instead of immediately
             // acting. When we do this we need to take care to account for the "Replacing Windows"
             // case (and seamless dim transfer).
-            if (state.mDimming == false) {
+            if (!state.mDimming) {
                 mDimLayerUsers.removeAt(i);
-                state.mSurfaceControl.destroy();
+                startDimExit(container, state.mSurfaceAnimator, t);
             } else {
                 didSomething = true;
                 // TODO: Once we use geometry from hierarchy this falls away.
-                t.setSize(state.mSurfaceControl, bounds.width(), bounds.height());
-                t.setPosition(state.mSurfaceControl, bounds.left, bounds.top);
+                t.setSize(state.mDimLayer, bounds.width(), bounds.height());
+                t.setPosition(state.mDimLayer, bounds.left, bounds.top);
+                if (!state.isVisible) {
+                    state.isVisible = true;
+                    t.show(state.mDimLayer);
+                    startDimEnter(container, state.mSurfaceAnimator, t);
+                }
             }
         }
         return didSomething;
     }
+
+    private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
+            SurfaceControl.Transaction t) {
+        startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */);
+    }
+
+    private void startDimExit(WindowContainer container, SurfaceAnimator animator,
+            SurfaceControl.Transaction t) {
+        startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */);
+    }
+
+    private void startAnim(WindowContainer container, SurfaceAnimator animator,
+            SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
+        animator.startAnimation(t, new LocalAnimationAdapter(
+                new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
+                mHost.mService.mSurfaceAnimationRunner), false /* hidden */);
+    }
+
+    private long getDimDuration(WindowContainer container) {
+        // If there's no container, then there isn't an animation occurring while dimming. Set the
+        // duration to 0 so it immediately dims to the set alpha.
+        if (container == null) {
+            return 0;
+        }
+
+        // Otherwise use the same duration as the animation on the WindowContainer
+        AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
+        return animationAdapter == null ? DEFAULT_DIM_ANIM_DURATION
+                : animationAdapter.getDurationHint();
+    }
+
+    private static class AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec {
+        private final long mDuration;
+        private final float mFromAlpha;
+        private final float mToAlpha;
+
+        AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) {
+            mFromAlpha = fromAlpha;
+            mToAlpha = toAlpha;
+            mDuration = duration;
+        }
+
+        @Override
+        public long getDuration() {
+            return mDuration;
+        }
+
+        @Override
+        public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
+            float alpha = ((float) currentPlayTime / getDuration()) * (mToAlpha - mFromAlpha)
+                    + mFromAlpha;
+            t.setAlpha(sc, alpha);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3f49f0c..7674b5e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3599,6 +3599,8 @@
     }
 
     private final class AboveAppWindowContainers extends NonAppWindowContainers {
+        private final Dimmer mDimmer = new Dimmer(this);
+        private final Rect mTmpDimBoundsRect = new Rect();
         AboveAppWindowContainers(String name, WindowManagerService service) {
             super(name, service);
         }
@@ -3630,6 +3632,22 @@
                 imeContainer.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE);
             }
         }
+
+        @Override
+        Dimmer getDimmer() {
+            return mDimmer;
+        }
+
+        @Override
+        void prepareSurfaces() {
+            mDimmer.resetDimStates();
+            super.prepareSurfaces();
+            getBounds(mTmpDimBoundsRect);
+
+            if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+                scheduleAnimation();
+            }
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 7d4eafb..8269a3b 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -88,6 +88,10 @@
      * Called when the transition is ready to be started, and all leashes have been set up.
      */
     void goodToGo() {
+        if (mPendingAnimations.isEmpty()) {
+            onAnimationFinished();
+            return;
+        }
         mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS);
         try {
             mRemoteAnimationAdapter.getRunner().onAnimationStart(createAnimations(),
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 0628436..7d970d9 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -462,8 +462,8 @@
                 } else {
                     mStack.getBounds(mTmpRect);
                     mTmpRect.intersect(getBounds());
+                    out.set(mTmpRect);
                 }
-                out.set(mTmpRect);
             } else {
                 out.set(getBounds());
             }
@@ -640,6 +640,7 @@
         mPreserveNonFloatingState = false;
     }
 
+    @Override
     Dimmer getDimmer() {
         return mDimmer;
     }
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index bc0f9ad..cf54b67 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1722,6 +1722,7 @@
                 || activityType == ACTIVITY_TYPE_ASSISTANT;
     }
 
+    @Override
     Dimmer getDimmer() {
         return mDimmer;
     }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index a1026bd..0c0ce0e 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1231,4 +1231,11 @@
             outPos.offset(-parentBounds.left, -parentBounds.top);
         }
     }
+
+    Dimmer getDimmer() {
+        if (mParent == null) {
+            return null;
+        }
+        return mParent.getDimmer();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4fb2390..066e4e6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -907,9 +907,16 @@
     public static WindowManagerService main(final Context context, final InputManagerService im,
             final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
             WindowManagerPolicy policy) {
+        return main(context, im, haveInputMethods, showBootMsgs, onlyCore, policy,
+                new SurfaceAnimationRunner());
+    }
+
+    public static WindowManagerService main(final Context context, final InputManagerService im,
+            final boolean haveInputMethods, final boolean showBootMsgs, final boolean onlyCore,
+            WindowManagerPolicy policy, SurfaceAnimationRunner surfaceAnimationRunner) {
         DisplayThread.getHandler().runWithScissors(() ->
                 sInstance = new WindowManagerService(context, im, haveInputMethods, showBootMsgs,
-                        onlyCore, policy), 0);
+                        onlyCore, policy, surfaceAnimationRunner), 0);
         return sInstance;
     }
 
@@ -932,7 +939,7 @@
 
     private WindowManagerService(Context context, InputManagerService inputManager,
             boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
-            WindowManagerPolicy policy) {
+            WindowManagerPolicy policy, SurfaceAnimationRunner surfaceAnimationRunner) {
         installLock(this, INDEX_WINDOW);
         mContext = context;
         mHaveInputMethods = haveInputMethods;
@@ -1059,7 +1066,7 @@
                 PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);
         mHoldingScreenWakeLock.setReferenceCounted(false);
 
-        mSurfaceAnimationRunner = new SurfaceAnimationRunner();
+        mSurfaceAnimationRunner = surfaceAnimationRunner;
 
         mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 477dd2b..55c982c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2138,18 +2138,6 @@
         mInputWindowHandle.inputChannel = null;
     }
 
-    private Dimmer getDimmer() {
-        Task task = getTask();
-        if (task != null) {
-            return task.getDimmer();
-        }
-        TaskStack taskStack = getStack();
-        if (taskStack != null) {
-            return taskStack.getDimmer();
-        }
-        return null;
-    }
-
     /** Returns true if the replacement window was removed. */
     boolean removeReplacedWindowIfNeeded(WindowState replacement) {
         if (mWillReplaceWindow && mReplacementWindow == replacement && replacement.hasDrawnLw()) {
@@ -4516,11 +4504,11 @@
     private void applyDims(Dimmer dimmer) {
         if (!mAnimatingExit && mAppDied) {
             mIsDimming = true;
-            getDimmer().dimAbove(getPendingTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
+            dimmer.dimAbove(getPendingTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
         } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
-                && !mAnimatingExit && isVisible()) {
+                && !mAnimatingExit && isVisible() && !mWinAnimator.mLastHidden) {
             mIsDimming = true;
-            getDimmer().dimBelow(getPendingTransaction(), this, mAttrs.dimAmount);
+            dimmer.dimBelow(getPendingTransaction(), this, mAttrs.dimAmount);
         }
     }
 
@@ -4531,7 +4519,6 @@
         if (dimmer != null) {
             applyDims(dimmer);
         }
-
         updateSurfacePosition(mPendingTransaction);
 
         mWinAnimator.prepareSurfaceLocked(true);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 5529426..d1cc5de 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -178,4 +178,10 @@
 
     public void clearSystemUpdatePolicyFreezePeriodRecord() {
     }
+
+    @Override
+    public boolean isMeteredDataDisabledForUser(ComponentName admin,
+            String packageName, int userId) {
+        return false;
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0589790..dae7605 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -744,7 +744,8 @@
                 if (deviceOwner != null) {
                     Bundle extras = new Bundle();
                     extras.putParcelable(Intent.EXTRA_USER, UserHandle.of(userHandle));
-                    sendAdminCommandLocked(deviceOwner, action, extras, null);
+                    sendAdminCommandLocked(deviceOwner, action, extras, /* result */ null,
+                            /* inForeground */ true);
                 }
             }
         }
@@ -11409,6 +11410,27 @@
         }
     }
 
+    @Override
+    public boolean isMeteredDataDisabledForUser(ComponentName who,
+            String packageName, int userId) {
+        Preconditions.checkNotNull(who);
+
+        if (!mHasFeature) {
+            return false;
+        }
+        if (!isCallerWithSystemUid()) {
+            throw new SecurityException(
+                    "Only the system can query restricted pkgs for a specific user");
+        }
+        synchronized (this) {
+            final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userId);
+            if (admin != null && admin.meteredDisabledPackages != null) {
+                return admin.meteredDisabledPackages.contains(packageName);
+            }
+        }
+        return false;
+    }
+
     private void pushMeteredDisabledPackagesLocked(int userId) {
         mInjector.getNetworkPolicyManagerInternal().setMeteredRestrictedPackages(
                 getMeteredDisabledPackagesLocked(userId), userId);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 6b87ea9..00a85a5 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2160,6 +2160,51 @@
                 () -> dpm.getMeteredDataDisabled(admin1));
     }
 
+    public void testGetMeteredDataDisabledForUser() throws Exception {
+        setAsProfileOwner(admin1);
+
+        // Setup
+        final ArrayList<String> emptyList = new ArrayList<>();
+        final ArrayList<String> pkgsToRestrict = new ArrayList<>();
+        final String package1 = "com.example.one";
+        final String package2 = "com.example.two";
+        final String package3 = "com.example.three";
+        pkgsToRestrict.add(package1);
+        pkgsToRestrict.add(package2);
+        setupPackageInPackageManager(package1, DpmMockContext.CALLER_USER_HANDLE, 123, 0);
+        setupPackageInPackageManager(package2, DpmMockContext.CALLER_USER_HANDLE, 456, 0);
+        List<String> excludedPkgs = dpm.setMeteredDataDisabled(admin1, pkgsToRestrict);
+
+        // Verify
+        assertEquals(emptyList, excludedPkgs);
+        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+        assertTrue(package1 + "should be restricted",
+                dpm.isMeteredDataDisabledForUser(admin1, package1,
+                        DpmMockContext.CALLER_USER_HANDLE));
+        assertTrue(package2 + "should be restricted",
+                dpm.isMeteredDataDisabledForUser(admin1, package2,
+                        DpmMockContext.CALLER_USER_HANDLE));
+        assertFalse(package3 + "should not be restricted",
+                dpm.isMeteredDataDisabledForUser(admin1, package3,
+                        DpmMockContext.CALLER_USER_HANDLE));
+    }
+
+    public void testGetMeteredDataDisabledForUser_nonSystemUidCaller() throws Exception {
+        setAsProfileOwner(admin1);
+        assertExpectException(SecurityException.class,
+                /* messageRegex= */ "Only the system can query restricted pkgs",
+                () -> dpm.isMeteredDataDisabledForUser(
+                        admin1, "com.example.one", DpmMockContext.CALLER_USER_HANDLE));
+        dpm.clearProfileOwner(admin1);
+
+        setDeviceOwner();
+        assertExpectException(SecurityException.class,
+                /* messageRegex= */ "Only the system can query restricted pkgs",
+                () -> dpm.isMeteredDataDisabledForUser(
+                        admin1, "com.example.one", DpmMockContext.CALLER_USER_HANDLE));
+        clearDeviceOwner();
+    }
+
     public void testCreateAdminSupportIntent() throws Exception {
         // Setup device owner.
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
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
new file mode 100644
index 0000000..f788cac
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
@@ -0,0 +1,205 @@
+/*
+ * 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.power.batterysaver;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+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 java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+
+/**
+ atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/power/batterysaver/BatterySavingStatsTest.java
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BatterySavingStatsTest {
+    private class BatterySavingStatsTestable extends BatterySavingStats {
+        private long mTime = 1_000_000; // Some random starting time.
+
+        private int mBatteryLevel = 100;
+
+        @Override
+        long injectCurrentTime() {
+            return mTime;
+        }
+
+        @Override
+        int injectBatteryLevel() {
+            return mBatteryLevel;
+        }
+
+        void assertDumpable() {
+            final ByteArrayOutputStream out = new ByteArrayOutputStream();
+            dump(new PrintWriter(out), ""); // Just make sure it won't crash.
+        }
+
+        void advanceClock(int minutes) {
+            mTime += 60_000 * minutes;
+        }
+
+        void drainBattery(int percent) {
+            mBatteryLevel -= percent;
+            if (mBatteryLevel < 0) {
+                mBatteryLevel = 0;
+            }
+        }
+
+        String toDebugString() {
+            final StringBuilder sb = new StringBuilder();
+            String sep = "";
+            for (int i = 0; i < mStats.size(); i++) {
+                sb.append(sep);
+                sb.append(stateToString(mStats.keyAt(i)));
+                sb.append(":");
+                sb.append(mStats.valueAt(i).toStringForTest());
+                sep = "\n";
+            }
+            return sb.toString();
+        }
+    }
+
+    @Test
+    public void testAll() {
+        final BatterySavingStatsTestable target = new BatterySavingStatsTestable();
+
+        target.assertDumpable();
+
+        target.advanceClock(1);
+        target.drainBattery(2);
+
+        target.transitionState(
+                BatterySaverState.OFF,
+                InteractiveState.INTERACTIVE,
+                DozeState.NOT_DOZING);
+
+        target.advanceClock(4);
+        target.drainBattery(1);
+
+        target.transitionState(
+                BatterySaverState.OFF,
+                InteractiveState.NON_INTERACTIVE,
+                DozeState.NOT_DOZING);
+
+        target.advanceClock(2);
+        target.drainBattery(5);
+
+        target.transitionState(
+                BatterySaverState.OFF,
+                InteractiveState.INTERACTIVE,
+                DozeState.NOT_DOZING);
+
+        target.advanceClock(4);
+        target.drainBattery(1);
+
+        target.transitionState(
+                BatterySaverState.OFF,
+                InteractiveState.NON_INTERACTIVE,
+                DozeState.NOT_DOZING);
+
+        target.advanceClock(2);
+        target.drainBattery(5);
+
+        target.transitionState(
+                BatterySaverState.OFF,
+                InteractiveState.INTERACTIVE,
+                DozeState.NOT_DOZING);
+
+        target.advanceClock(3);
+        target.drainBattery(1);
+
+        target.transitionState(
+                BatterySaverState.OFF,
+                InteractiveState.NON_INTERACTIVE,
+                DozeState.LIGHT);
+
+        target.advanceClock(5);
+        target.drainBattery(1);
+
+        target.transitionState(
+                BatterySaverState.OFF,
+                InteractiveState.NON_INTERACTIVE,
+                DozeState.DEEP);
+
+        target.advanceClock(1);
+        target.drainBattery(2);
+
+        target.transitionState(
+                BatterySaverState.ON,
+                InteractiveState.INTERACTIVE,
+                DozeState.NOT_DOZING);
+
+        target.advanceClock(1);
+        target.drainBattery(3);
+
+        target.transitionState(
+                BatterySaverState.OFF,
+                InteractiveState.INTERACTIVE,
+                DozeState.NOT_DOZING);
+
+        target.advanceClock(3);
+        target.drainBattery(5);
+
+        target.transitionState(
+                BatterySaverState.ON,
+                InteractiveState.INTERACTIVE,
+                DozeState.NOT_DOZING);
+
+        target.advanceClock(3);
+        target.drainBattery(5);
+
+        target.startCharging();
+
+        target.advanceClock(5);
+        target.drainBattery(10);
+
+        target.transitionState(
+                BatterySaverState.ON,
+                InteractiveState.INTERACTIVE,
+                DozeState.NOT_DOZING);
+
+        target.advanceClock(5);
+        target.drainBattery(1);
+
+        target.startCharging();
+
+        target.assertDumpable();
+
+        assertEquals(
+                "BS=0,I=0,D=0:{4m,10,150.00}\n" +
+                "BS=1,I=0,D=0:{0m,0,0.00}\n" +
+                "BS=0,I=1,D=0:{14m,8,34.29}\n" +
+                "BS=1,I=1,D=0:{9m,9,60.00}\n" +
+                "BS=0,I=0,D=1:{5m,1,12.00}\n" +
+                "BS=1,I=0,D=1:{0m,0,0.00}\n" +
+                "BS=0,I=1,D=1:{0m,0,0.00}\n" +
+                "BS=1,I=1,D=1:{0m,0,0.00}\n" +
+                "BS=0,I=0,D=2:{1m,2,120.00}\n" +
+                "BS=1,I=0,D=2:{0m,0,0.00}\n" +
+                "BS=0,I=1,D=2:{0m,0,0.00}\n" +
+                "BS=1,I=1,D=2:{0m,0,0.00}",
+                target.toDebugString());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
index 70906df..396fef4 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -16,11 +16,14 @@
 
 package com.android.server.wm;
 
-import java.util.HashMap;
-
-import org.junit.Test;
-import org.junit.Before;
-import org.junit.runner.RunWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
 
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
@@ -28,22 +31,25 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.eq;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.stubbing.Answer;
 
 /**
  * Build/Install/Run:
- *  bit FrameworksServicesTests:com.android.server.wm.DimmerTests;
+ * atest FrameworksServicesTests:com.android.server.wm.DimmerTests;
  */
 @Presubmit
+@Ignore("b/72450130")
 @RunWith(AndroidJUnit4.class)
 public class DimmerTests extends WindowTestsBase {
+
+    public DimmerTests() {
+        super(spy(new SurfaceAnimationRunner()));
+    }
+
     private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         final SurfaceControl mControl = mock(SurfaceControl.class);
         final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
@@ -65,12 +71,14 @@
 
     private class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> {
         final SurfaceSession mSession = new SurfaceSession();
-        SurfaceControl mBuiltSurface = null;
-        final SurfaceControl mHostControl = mock(SurfaceControl.class);
         final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class);
 
         MockSurfaceBuildingContainer() {
             super(sWm);
+            mSurfaceControl = sWm.makeSurfaceBuilder(mSession)
+                    .setName("test surface")
+                    .setSize(1, 1)
+                    .build();
         }
 
         class MockSurfaceBuilder extends SurfaceControl.Builder {
@@ -80,12 +88,19 @@
 
             @Override
             public SurfaceControl build() {
-                SurfaceControl sc = mock(SurfaceControl.class);
-                mBuiltSurface = sc;
-                return sc;
+                return spy(sWm.makeSurfaceBuilder(mSession)
+                        .setName("test surface")
+                        .setSize(1, 1)
+                        .build());
             }
         }
 
+        @Override
+        SurfaceControl.Builder makeSurface() {
+            return sWm.makeSurfaceBuilder(mSession)
+                    .setName("test surface")
+                    .setSize(1, 1);
+        }
 
         @Override
         SurfaceControl.Builder makeChildSurface(WindowContainer child) {
@@ -93,11 +108,6 @@
         }
 
         @Override
-        public SurfaceControl getSurfaceControl() {
-            return mHostControl;
-        }
-
-        @Override
         public SurfaceControl.Transaction getPendingTransaction() {
             return mHostTransaction;
         }
@@ -114,29 +124,37 @@
 
         mTransaction = mock(SurfaceControl.Transaction.class);
         mDimmer = new Dimmer(mHost);
+
+        doAnswer((Answer<Void>) invocation -> {
+            Runnable runnable = invocation.getArgument(3);
+            runnable.run();
+            return null;
+        }).when(sWm.mSurfaceAnimationRunner).startAnimation(any(), any(), any(), any());
     }
 
     @Test
     public void testDimAboveNoChildCreatesSurface() throws Exception {
         final float alpha = 0.8f;
         mDimmer.dimAbove(mTransaction, alpha);
-        assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
 
-        verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
-        verify(mTransaction).show(mHost.mBuiltSurface);
-        verify(mTransaction).setLayer(mHost.mBuiltSurface, Integer.MAX_VALUE);
+        SurfaceControl dimLayer = getDimLayer(null);
+
+        assertNotNull("Dimmer should have created a surface", dimLayer);
+
+        verify(mTransaction).setAlpha(dimLayer, alpha);
+        verify(mTransaction).setLayer(dimLayer, Integer.MAX_VALUE);
     }
 
     @Test
     public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() throws Exception {
         float alpha = 0.8f;
         mDimmer.dimAbove(mTransaction, alpha);
-        final SurfaceControl firstSurface = mHost.mBuiltSurface;
+        final SurfaceControl firstSurface = getDimLayer(null);
 
         alpha = 0.9f;
         mDimmer.dimAbove(mTransaction, alpha);
 
-        assertEquals(firstSurface, mHost.mBuiltSurface);
+        assertEquals(firstSurface, getDimLayer(null));
         verify(mTransaction).setAlpha(firstSurface, 0.9f);
     }
 
@@ -148,16 +166,20 @@
         int height = 300;
         Rect bounds = new Rect(0, 0, width, height);
         mDimmer.updateDims(mTransaction, bounds);
-        verify(mTransaction).setSize(mHost.mBuiltSurface, width, height);
+
+        verify(mTransaction).setSize(getDimLayer(null), width, height);
+        verify(mTransaction).show(getDimLayer(null));
     }
 
     @Test
     public void testDimAboveNoChildNotReset() throws Exception {
         mDimmer.dimAbove(mTransaction, 0.8f);
+        SurfaceControl dimLayer = getDimLayer(null);
         mDimmer.resetDimStates();
 
         mDimmer.updateDims(mTransaction, new Rect());
-        verify(mHost.mBuiltSurface, never()).destroy();
+        verify(mTransaction).show(getDimLayer(null));
+        verify(dimLayer, never()).destroy();
     }
 
     @Test
@@ -167,11 +189,12 @@
 
         final float alpha = 0.8f;
         mDimmer.dimAbove(mTransaction, child, alpha);
-        assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+        SurfaceControl mDimLayer = getDimLayer(child);
 
-        verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
-        verify(mTransaction).show(mHost.mBuiltSurface);
-        verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, 1);
+        assertNotNull("Dimmer should have created a surface", mDimLayer);
+
+        verify(mTransaction).setAlpha(mDimLayer, alpha);
+        verify(mTransaction).setRelativeLayer(mDimLayer, child.mControl, 1);
     }
 
     @Test
@@ -181,11 +204,12 @@
 
         final float alpha = 0.8f;
         mDimmer.dimBelow(mTransaction, child, alpha);
-        assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+        SurfaceControl mDimLayer = getDimLayer(child);
 
-        verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
-        verify(mTransaction).show(mHost.mBuiltSurface);
-        verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, -1);
+        assertNotNull("Dimmer should have created a surface", mDimLayer);
+
+        verify(mTransaction).setAlpha(mDimLayer, alpha);
+        verify(mTransaction).setRelativeLayer(mDimLayer, child.mControl, -1);
     }
 
     @Test
@@ -195,9 +219,11 @@
 
         final float alpha = 0.8f;
         mDimmer.dimAbove(mTransaction, child, alpha);
+        SurfaceControl dimLayer = getDimLayer(child);
         mDimmer.resetDimStates();
+
         mDimmer.updateDims(mTransaction, new Rect());
-        verify(mHost.mBuiltSurface).destroy();
+        verify(dimLayer).destroy();
     }
 
     @Test
@@ -207,10 +233,16 @@
 
         final float alpha = 0.8f;
         mDimmer.dimAbove(mTransaction, child, alpha);
+        SurfaceControl dimLayer = getDimLayer(child);
         mDimmer.resetDimStates();
         mDimmer.dimAbove(mTransaction, child, alpha);
 
         mDimmer.updateDims(mTransaction, new Rect());
-        verify(mHost.mBuiltSurface, never()).destroy();
+        verify(mTransaction).show(dimLayer);
+        verify(dimLayer, never()).destroy();
+    }
+
+    private SurfaceControl getDimLayer(WindowContainer windowContainer) {
+        return mDimmer.mDimLayerUsers.get(windowContainer).mDimLayer;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 897be34..f860195 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -134,4 +135,10 @@
         verify(mMockRunner).onAnimationCancelled();
         verify(mFinishedCallback).onAnimationFinished(eq(adapter));
     }
+
+    @Test
+    public void testZeroAnimations() throws Exception {
+        mController.goodToGo();
+        verifyZeroInteractions(mMockRunner);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 35ca493..81fd889 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -27,6 +27,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
 
 import android.os.PowerSaveState;
 import android.util.proto.ProtoOutputStream;
@@ -68,6 +69,11 @@
     private Runnable mRunnableWhenAddingSplashScreen;
 
     static synchronized WindowManagerService getWindowManagerService(Context context) {
+        return getWindowManagerService(context, new SurfaceAnimationRunner());
+    }
+
+    static synchronized WindowManagerService getWindowManagerService(Context context,
+            SurfaceAnimationRunner surfaceAnimationRunner) {
         if (sWm == null) {
             // We only want to do this once for the test process as we don't want WM to try to
             // register a bunch of local services again.
@@ -105,7 +111,7 @@
             }
 
             sWm = WindowManagerService.main(context, ims, true, false,
-                    false, new TestWindowManagerPolicy());
+                    false, new TestWindowManagerPolicy(), surfaceAnimationRunner);
         }
         return sWm;
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 69b1378..7918901 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -84,6 +84,16 @@
     WindowState mChildAppWindowBelow;
     HashSet<WindowState> mCommonWindows;
 
+    private final SurfaceAnimationRunner mSurfaceAnimationRunner;
+
+    public WindowTestsBase() {
+        this(new SurfaceAnimationRunner());
+    }
+
+    public WindowTestsBase(SurfaceAnimationRunner surfaceAnimationRunner) {
+        mSurfaceAnimationRunner = surfaceAnimationRunner;
+    }
+
     @Before
     public void setUp() throws Exception {
         if (!sOneTimeSetupDone) {
@@ -98,7 +108,7 @@
         final Context context = InstrumentationRegistry.getTargetContext();
         AttributeCache.init(context);
 
-        sWm = TestWindowManagerPolicy.getWindowManagerService(context);
+        sWm = TestWindowManagerPolicy.getWindowManagerService(context, mSurfaceAnimationRunner);
         beforeCreateDisplay();
 
         context.getDisplay().getDisplayInfo(mDisplayInfo);
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
index d3bb804..1606bd9 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/PinnedSliceStateTest.java
@@ -198,6 +198,7 @@
         when(binder.isBinderAlive()).thenReturn(false);
         arg.getValue().binderDied();
 
+        verify(mSliceService).unlisten(eq(TEST_URI));
         verify(mSliceService).removePinnedSlice(eq(TEST_URI));
         assertFalse(mPinnedSliceManager.isPinned());
     }
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index 03ce2d8..521adef 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -36,12 +36,12 @@
      */
     public static final int TX_POWER_LEVELS = 5;
 
-    private final long mTimestamp;
-    private final int mSleepTimeMs;
-    private final int mIdleTimeMs;
-    private final int [] mTxTimeMs = new int[TX_POWER_LEVELS];
-    private final int mRxTimeMs;
-    private final int mEnergyUsed;
+    private long mTimestamp;
+    private int mSleepTimeMs;
+    private int mIdleTimeMs;
+    private int [] mTxTimeMs = new int[TX_POWER_LEVELS];
+    private int mRxTimeMs;
+    private int mEnergyUsed;
 
     public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs,
                         int[] txTimeMs, int rxTimeMs, int energyUsed) {
@@ -110,6 +110,10 @@
         return mTimestamp;
     }
 
+    public void setTimestamp(long timestamp) {
+        mTimestamp = timestamp;
+    }
+
     /**
      * @return tx time in ms. It's an array of tx times
      * with each index...
@@ -118,6 +122,10 @@
         return mTxTimeMs;
     }
 
+    public void setTxTimeMillis(int[] txTimeMs) {
+        mTxTimeMs = txTimeMs;
+    }
+
     /**
      * @return sleep time in ms.
      */
@@ -125,6 +133,10 @@
         return mSleepTimeMs;
     }
 
+    public void setSleepTimeMillis(int sleepTimeMillis) {
+        mSleepTimeMs = sleepTimeMillis;
+    }
+
     /**
      * @return idle time in ms.
      */
@@ -132,6 +144,10 @@
         return mIdleTimeMs;
     }
 
+    public void setIdleTimeMillis(int idleTimeMillis) {
+        mIdleTimeMs = idleTimeMillis;
+    }
+
     /**
      * @return rx time in ms.
      */
@@ -139,6 +155,10 @@
         return mRxTimeMs;
     }
 
+    public void setRxTimeMillis(int rxTimeMillis) {
+        mRxTimeMs = rxTimeMillis;
+    }
+
     /**
      * product of current(mA), voltage(V) and time(ms)
      * @return energy used
@@ -147,6 +167,10 @@
         return mEnergyUsed;
     }
 
+    public void setEnergyUsed(int energyUsed) {
+        mEnergyUsed = energyUsed;
+    }
+
     /**
      * @return if the record is valid
      */
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 2a2ff0c..309bc80 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -158,9 +158,6 @@
 
     int getVerboseLoggingLevel();
 
-    void enableAggressiveHandover(int enabled);
-    int getAggressiveHandover();
-
     void enableWifiConnectivityManager(boolean enabled);
 
     void disableEphemeralNetwork(String SSID, String packageName);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 13b7c1a..897b1ea 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -3532,31 +3532,6 @@
     }
 
     /**
-     * Set wifi Aggressive Handover. Called from developer settings.
-     * @hide
-     */
-    public void enableAggressiveHandover(int enabled) {
-        try {
-            mService.enableAggressiveHandover(enabled);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Get the WiFi Handover aggressiveness.This is used by settings
-     * to decide what to show within the picker.
-     * @hide
-     */
-    public int getAggressiveHandover() {
-        try {
-            return mService.getAggressiveHandover();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Removes all saved wifi networks.
      *
      * @hide