Merge "Add user restriction to suppress error dialogs."
diff --git a/Android.mk b/Android.mk
index 98e4299..6186c55 100644
--- a/Android.mk
+++ b/Android.mk
@@ -548,9 +548,10 @@
 	telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
 	telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl \
 	wifi/java/android/net/wifi/IWifiManager.aidl \
-	wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl \
-	wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl \
 	wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl \
+	wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl \
+	wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl \
+	wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl \
 	wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
 	wifi/java/android/net/wifi/rtt/IRttCallback.aidl \
 	wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl \
@@ -1030,6 +1031,7 @@
     -since $(SRC_API_DIR)/24.txt 24 \
     -since $(SRC_API_DIR)/25.txt 25 \
     -since $(SRC_API_DIR)/26.txt 26 \
+    -since $(SRC_API_DIR)/27.txt 27 \
     -werror -lerror -hide 111 -hide 113 -hide 121 -hide 125 -hide 126 -hide 127 -hide 128 \
     -overview $(LOCAL_PATH)/core/java/overview.html \
 
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
new file mode 100644
index 0000000..47dd257
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.text;
+
+import static android.text.Layout.Alignment.ALIGN_NORMAL;
+
+import android.graphics.Canvas;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.text.NonEditableTextGenerator.TextType;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Performance test for {@link BoringLayout} create and draw.
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+public class BoringLayoutCreateDrawPerfTest {
+
+    private static final boolean[] BOOLEANS = new boolean[]{false, true};
+    private static final float SPACING_ADD = 10f;
+    private static final float SPACING_MULT = 1.5f;
+
+    @Parameterized.Parameters(name = "cached={3},{1} chars,{0}")
+    public static Collection cases() {
+        final List<Object[]> params = new ArrayList<>();
+        for (int length : new int[]{128}) {
+            for (boolean cached : BOOLEANS) {
+                for (TextType textType : new TextType[]{TextType.STRING,
+                        TextType.SPANNABLE_BUILDER}) {
+                    params.add(new Object[]{textType.name(), length, textType, cached});
+                }
+            }
+        }
+        return params;
+    }
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private final int mLength;
+    private final TextType mTextType;
+    private final boolean mCached;
+    private final TextPaint mTextPaint;
+
+    public BoringLayoutCreateDrawPerfTest(String label, int length, TextType textType,
+            boolean cached) {
+        mLength = length;
+        mCached = cached;
+        mTextType = textType;
+        mTextPaint = new TextPaint();
+        mTextPaint.setTextSize(10);
+    }
+
+    /**
+     * Measures the creation time for {@link BoringLayout}.
+     */
+    @Test
+    public void timeCreate() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        state.pauseTiming();
+        Canvas.freeTextLayoutCaches();
+        final CharSequence text = createRandomText();
+        // isBoring result is calculated in another test, we want to measure only the
+        // create time for Boring without isBoring check. Therefore it is calculated here.
+        final BoringLayout.Metrics metrics = BoringLayout.isBoring(text, mTextPaint);
+        if (mCached) createLayout(text, metrics);
+        state.resumeTiming();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            if (!mCached) Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            createLayout(text, metrics);
+        }
+    }
+
+    /**
+     * Measures the draw time for {@link BoringLayout} or {@link StaticLayout}.
+     */
+    @Test
+    public void timeDraw() throws Throwable {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        state.pauseTiming();
+        Canvas.freeTextLayoutCaches();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        final CharSequence text = createRandomText();
+        final BoringLayout.Metrics metrics = BoringLayout.isBoring(text, mTextPaint);
+        final Layout layout = createLayout(text, metrics);
+        state.resumeTiming();
+
+        while (state.keepRunning()) {
+
+            state.pauseTiming();
+            final DisplayListCanvas canvas = node.start(1200, 200);
+            final int save = canvas.save();
+            if (!mCached) Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            layout.draw(canvas);
+
+            state.pauseTiming();
+            canvas.restoreToCount(save);
+            node.end(canvas);
+            state.resumeTiming();
+        }
+    }
+
+    private CharSequence createRandomText() {
+        return new NonEditableTextGenerator(new Random(0))
+                .setSequenceLength(mLength)
+                .setCreateBoring(true)
+                .setTextType(mTextType)
+                .build();
+    }
+
+    private Layout createLayout(CharSequence text,
+            BoringLayout.Metrics metrics) {
+        return BoringLayout.make(text, mTextPaint, Integer.MAX_VALUE /*width*/,
+                ALIGN_NORMAL, SPACING_MULT, SPACING_ADD, metrics, true /*includePad*/);
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
new file mode 100644
index 0000000..34de65d
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.text;
+
+import android.graphics.Canvas;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.text.NonEditableTextGenerator.TextType;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Performance test for {@link BoringLayout#isBoring(CharSequence, TextPaint)}.
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+public class BoringLayoutIsBoringPerfTest {
+
+    private static final boolean[] BOOLEANS = new boolean[]{false, true};
+
+    @Parameterized.Parameters(name = "cached={4},{1} chars,{0}")
+    public static Collection cases() {
+        final List<Object[]> params = new ArrayList<>();
+        for (int length : new int[]{128}) {
+            for (boolean boring : BOOLEANS) {
+                for (boolean cached : BOOLEANS) {
+                    for (TextType textType : new TextType[]{TextType.STRING,
+                            TextType.SPANNABLE_BUILDER}) {
+                        params.add(new Object[]{
+                                (boring ? "Boring" : "NotBoring") + "," + textType.name(),
+                                length, boring, textType, cached});
+                    }
+                }
+            }
+        }
+        return params;
+    }
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private final int mLength;
+    private final TextType mTextType;
+    private final boolean mCreateBoring;
+    private final boolean mCached;
+    private final TextPaint mTextPaint;
+
+    public BoringLayoutIsBoringPerfTest(String label, int length, boolean boring, TextType textType,
+            boolean cached) {
+        mLength = length;
+        mCreateBoring = boring;
+        mCached = cached;
+        mTextType = textType;
+        mTextPaint = new TextPaint();
+        mTextPaint.setTextSize(10);
+    }
+
+    /**
+     * Measure the time for the {@link BoringLayout#isBoring(CharSequence, TextPaint)}.
+     */
+    @Test
+    public void timeIsBoring() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        state.pauseTiming();
+        Canvas.freeTextLayoutCaches();
+        final CharSequence text = createRandomText();
+        if (mCached) BoringLayout.isBoring(text, mTextPaint);
+        state.resumeTiming();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            if (!mCached) Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            BoringLayout.isBoring(text, mTextPaint);
+        }
+    }
+
+    private CharSequence createRandomText() {
+        return new NonEditableTextGenerator(new Random(0))
+                .setSequenceLength(mLength)
+                .setCreateBoring(mCreateBoring)
+                .setTextType(mTextType)
+                .build();
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/text/NonEditableTextGenerator.java b/apct-tests/perftests/core/src/android/text/NonEditableTextGenerator.java
new file mode 100644
index 0000000..7c0cf0e
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/NonEditableTextGenerator.java
@@ -0,0 +1,138 @@
+package android.text;
+
+import static android.text.Spanned.SPAN_INCLUSIVE_INCLUSIVE;
+
+import android.text.style.BulletSpan;
+
+import java.util.Random;
+
+/**
+ *
+ */
+public class NonEditableTextGenerator {
+
+    enum TextType {
+        STRING,
+        SPANNED,
+        SPANNABLE_BUILDER
+    }
+
+    private boolean mCreateBoring;
+    private TextType mTextType;
+    private int mSequenceLength;
+    private final Random mRandom;
+
+    public NonEditableTextGenerator(Random random) {
+        mRandom = random;
+    }
+
+    public NonEditableTextGenerator setCreateBoring(boolean createBoring) {
+        mCreateBoring = createBoring;
+        return this;
+    }
+
+    public NonEditableTextGenerator setTextType(TextType textType) {
+        mTextType = textType;
+        return this;
+    }
+
+    public NonEditableTextGenerator setSequenceLength(int sequenceLength) {
+        mSequenceLength = sequenceLength;
+        return this;
+    }
+
+    /**
+     * Sample charSequence generated:
+     * NRjPzjvUadHmH ExoEoTqfx pCLw qtndsqfpk AqajVCbgjGZ igIeC dfnXRgA
+     */
+    public CharSequence build() {
+        final RandomCharSequenceGenerator sequenceGenerator = new RandomCharSequenceGenerator(
+                mRandom);
+        if (mSequenceLength > 0) {
+            sequenceGenerator.setSequenceLength(mSequenceLength);
+        }
+
+        final CharSequence charSequence = sequenceGenerator.buildLatinSequence();
+
+        switch (mTextType) {
+            case SPANNED:
+            case SPANNABLE_BUILDER:
+                return createSpannable(charSequence);
+            case STRING:
+            default:
+                return createString(charSequence);
+        }
+    }
+
+    private Spannable createSpannable(CharSequence charSequence) {
+        final Spannable spannable = (mTextType == TextType.SPANNABLE_BUILDER) ?
+                new SpannableStringBuilder(charSequence) : new SpannableString(charSequence);
+
+        if (!mCreateBoring) {
+            // add a paragraph style to make it non boring
+            spannable.setSpan(new BulletSpan(), 0, spannable.length(), SPAN_INCLUSIVE_INCLUSIVE);
+        }
+
+        spannable.setSpan(new Object(), 0, spannable.length(), SPAN_INCLUSIVE_INCLUSIVE);
+        spannable.setSpan(new Object(), 0, 1, SPAN_INCLUSIVE_INCLUSIVE);
+
+        return spannable;
+    }
+
+    private String createString(CharSequence charSequence) {
+        if (mCreateBoring) {
+            return charSequence.toString();
+        } else {
+            // BoringLayout checks to see if there is a surrogate pair and if so tells that
+            // the charSequence is not suitable for boring. Add an emoji to make it non boring.
+            // Emoji is added instead of RTL, since emoji stays in the same run and is a more
+            // common case.
+            return charSequence.toString() + "\uD83D\uDC68\uD83C\uDFFF";
+        }
+    }
+
+    public static class RandomCharSequenceGenerator {
+
+        private static final int DEFAULT_MIN_WORD_LENGTH = 3;
+        private static final int DEFAULT_MAX_WORD_LENGTH = 15;
+        private static final int DEFAULT_SEQUENCE_LENGTH = 256;
+
+        private int mMinWordLength = DEFAULT_MIN_WORD_LENGTH;
+        private int mMaxWordLength = DEFAULT_MAX_WORD_LENGTH;
+        private int mSequenceLength = DEFAULT_SEQUENCE_LENGTH;
+        private final Random mRandom;
+
+        public RandomCharSequenceGenerator(Random random) {
+            mRandom = random;
+        }
+
+        public RandomCharSequenceGenerator setSequenceLength(int sequenceLength) {
+            mSequenceLength = sequenceLength;
+            return this;
+        }
+
+        public CharSequence buildLatinSequence() {
+            final StringBuilder result = new StringBuilder();
+            while (result.length() < mSequenceLength) {
+                // add random word
+                result.append(buildLatinWord());
+                result.append(' ');
+            }
+            return result.substring(0, mSequenceLength);
+        }
+
+        public CharSequence buildLatinWord() {
+            final StringBuilder result = new StringBuilder();
+            // create a random length that is (mMinWordLength + random amount of chars) where
+            // total size is less than mMaxWordLength
+            final int length = mRandom.nextInt(mMaxWordLength - mMinWordLength) + mMinWordLength;
+            while (result.length() < length) {
+                // add random letter
+                int base = mRandom.nextInt(2) == 0 ? 'A' : 'a';
+                result.append(Character.toChars(mRandom.nextInt(26) + base));
+            }
+            return result.toString();
+        }
+    }
+
+}
diff --git a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
new file mode 100644
index 0000000..00b60ad
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.text;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Performance test for single line measure and draw using {@link Paint} and {@link Canvas}.
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+public class PaintMeasureDrawPerfTest {
+
+    private static final boolean[] BOOLEANS = new boolean[]{false, true};
+
+    @Parameterized.Parameters(name = "cached={1},{0} chars")
+    public static Collection cases() {
+        final List<Object[]> params = new ArrayList<>();
+        for (int length : new int[]{128}) {
+            for (boolean cached : BOOLEANS) {
+                params.add(new Object[]{length, cached});
+            }
+        }
+        return params;
+    }
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private final int mLength;
+    private final boolean mCached;
+    private final TextPaint mTextPaint;
+
+
+    public PaintMeasureDrawPerfTest(int length, boolean cached) {
+        mLength = length;
+        mCached = cached;
+        mTextPaint = new TextPaint();
+        mTextPaint.setTextSize(10);
+    }
+
+    /**
+     * Measure the time for {@link Paint#measureText(String)}
+     */
+    @Test
+    public void timeMeasure() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        state.pauseTiming();
+        Canvas.freeTextLayoutCaches();
+        final String text = createRandomText();
+        if (mCached) mTextPaint.measureText(text);
+        state.resumeTiming();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            if (!mCached) Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            mTextPaint.measureText(text);
+        }
+    }
+
+    /**
+     * Measures the time for {@link Canvas#drawText(String, float, float, Paint)}
+     */
+    @Test
+    public void timeDraw() throws Throwable {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        state.pauseTiming();
+        Canvas.freeTextLayoutCaches();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        final String text = createRandomText();
+        if (mCached) mTextPaint.measureText(text);
+        state.resumeTiming();
+
+        while (state.keepRunning()) {
+
+            state.pauseTiming();
+            final DisplayListCanvas canvas = node.start(1200, 200);
+            final int save = canvas.save();
+            if (!mCached) Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            canvas.drawText(text, 0 /*x*/, 100 /*y*/, mTextPaint);
+
+            state.pauseTiming();
+            canvas.restoreToCount(save);
+            node.end(canvas);
+            state.resumeTiming();
+        }
+    }
+
+    private String createRandomText() {
+        return (String) new NonEditableTextGenerator(new Random(0))
+                .setSequenceLength(mLength)
+                .setCreateBoring(true)
+                .setTextType(NonEditableTextGenerator.TextType.STRING)
+                .build();
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
new file mode 100644
index 0000000..356e2e0
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.text;
+
+import static android.text.Layout.Alignment.ALIGN_NORMAL;
+
+import android.graphics.Canvas;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.text.NonEditableTextGenerator.TextType;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Performance test for multi line, single style {@link StaticLayout} creation/draw.
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+public class StaticLayoutCreateDrawPerfTest {
+
+    private static final boolean[] BOOLEANS = new boolean[]{false, true};
+
+    private static final float SPACING_ADD = 10f;
+    private static final float SPACING_MULT = 1.5f;
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Parameterized.Parameters(name = "cached={3},{1} chars,{0}")
+    public static Collection cases() {
+        final List<Object[]> params = new ArrayList<>();
+        for (int length : new int[]{128}) {
+            for (boolean cached : BOOLEANS) {
+                for (TextType textType : new TextType[]{TextType.STRING,
+                        TextType.SPANNABLE_BUILDER}) {
+                    params.add(new Object[]{textType.name(), length, textType, cached});
+                }
+            }
+        }
+        return params;
+    }
+
+    private final int mLineWidth;
+    private final int mLength;
+    private final TextType mTextType;
+    private final boolean mCached;
+    private final TextPaint mTextPaint;
+
+    public StaticLayoutCreateDrawPerfTest(String label, int length, TextType textType,
+            boolean cached) {
+        mLength = length;
+        mTextType = textType;
+        mCached = cached;
+        mTextPaint = new TextPaint();
+        mTextPaint.setTextSize(10);
+        mLineWidth = Integer.MAX_VALUE;
+    }
+
+    /**
+     * Measures the creation time for a multi line {@link StaticLayout}.
+     */
+    @Test
+    public void timeCreate() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        state.pauseTiming();
+        Canvas.freeTextLayoutCaches();
+        final CharSequence text = createRandomText(mLength);
+        createLayout(text);
+        state.resumeTiming();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            if (!mCached) Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            createLayout(text);
+        }
+    }
+
+    /**
+     * Measures the draw time for a multi line {@link StaticLayout}.
+     */
+    @Test
+    public void timeDraw() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        state.pauseTiming();
+        Canvas.freeTextLayoutCaches();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        final CharSequence text = createRandomText(mLength);
+        final Layout layout = createLayout(text);
+        state.resumeTiming();
+
+        while (state.keepRunning()) {
+
+            state.pauseTiming();
+            final DisplayListCanvas canvas = node.start(1200, 200);
+            int save = canvas.save();
+            if (!mCached) Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            layout.draw(canvas);
+
+            state.pauseTiming();
+            canvas.restoreToCount(save);
+            node.end(canvas);
+            state.resumeTiming();
+        }
+    }
+
+    private Layout createLayout(CharSequence text) {
+        return StaticLayout.Builder.obtain(text, 0 /*start*/, text.length() /*end*/, mTextPaint,
+                mLineWidth)
+                .setAlignment(ALIGN_NORMAL)
+                .setIncludePad(true)
+                .setLineSpacing(SPACING_ADD, SPACING_MULT)
+                .build();
+    }
+
+    private CharSequence createRandomText(int length) {
+        return new NonEditableTextGenerator(new Random(0))
+                .setSequenceLength(length)
+                .setTextType(mTextType)
+                .build();
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
new file mode 100644
index 0000000..a2bf33e1
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.text;
+
+import static android.view.View.MeasureSpec.AT_MOST;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
+
+import android.graphics.Canvas;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.text.NonEditableTextGenerator.TextType;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+import android.widget.TextView;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.Random;
+
+/**
+ * Performance test for multi line, single style {@link StaticLayout} creation/draw.
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+public class TextViewSetTextMeasurePerfTest {
+
+    private static final boolean[] BOOLEANS = new boolean[]{false, true};
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Parameterized.Parameters(name = "cached={3},{1} chars,{0}")
+    public static Collection cases() {
+        final List<Object[]> params = new ArrayList<>();
+        for (int length : new int[]{128}) {
+            for (boolean cached : BOOLEANS) {
+                for (TextType textType : new TextType[]{TextType.STRING,
+                        TextType.SPANNABLE_BUILDER}) {
+                    params.add(new Object[]{textType.name(), length, textType, cached});
+                }
+            }
+        }
+        return params;
+    }
+
+    private final int mLineWidth;
+    private final int mLength;
+    private final TextType mTextType;
+    private final boolean mCached;
+    private final TextPaint mTextPaint;
+
+    public TextViewSetTextMeasurePerfTest(String label, int length, TextType textType,
+            boolean cached) {
+        mLength = length;
+        mTextType = textType;
+        mCached = cached;
+        mTextPaint = new TextPaint();
+        mTextPaint.setTextSize(10);
+        mLineWidth = Integer.MAX_VALUE;
+    }
+
+    /**
+     * Measures the time to setText and measure for a {@link TextView}.
+     */
+    @Test
+    public void timeCreate() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        state.pauseTiming();
+        Canvas.freeTextLayoutCaches();
+        final CharSequence text = createRandomText(mLength);
+        final TextView textView = new TextView(InstrumentationRegistry.getTargetContext());
+        textView.setText(text);
+        state.resumeTiming();
+
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            textView.setTextLocale(Locale.UK);
+            textView.setTextLocale(Locale.US);
+            if (!mCached) Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.setText(text);
+            textView.measure(AT_MOST | mLineWidth, UNSPECIFIED);
+        }
+    }
+
+    /**
+     * Measures the time to draw for a {@link TextView}.
+     */
+    @Test
+    public void timeDraw() throws Exception {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        state.pauseTiming();
+        Canvas.freeTextLayoutCaches();
+        final RenderNode node = RenderNode.create("benchmark", null);
+        final CharSequence text = createRandomText(mLength);
+        final TextView textView = new TextView(InstrumentationRegistry.getTargetContext());
+        textView.setText(text);
+        state.resumeTiming();
+
+        while (state.keepRunning()) {
+
+            state.pauseTiming();
+            final DisplayListCanvas canvas = node.start(1200, 200);
+            int save = canvas.save();
+            textView.setTextLocale(Locale.UK);
+            textView.setTextLocale(Locale.US);
+            if (!mCached) Canvas.freeTextLayoutCaches();
+            state.resumeTiming();
+
+            textView.draw(canvas);
+
+            state.pauseTiming();
+            canvas.restoreToCount(save);
+            node.end(canvas);
+            state.resumeTiming();
+        }
+    }
+
+    private CharSequence createRandomText(int length) {
+        return new NonEditableTextGenerator(new Random(0))
+                .setSequenceLength(length)
+                .setCreateBoring(false)
+                .setTextType(mTextType)
+                .build();
+    }
+}
diff --git a/api/current.txt b/api/current.txt
index 5746a3f..9bbdee1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -37207,8 +37207,10 @@
   }
 
   public static class ImageTransformation.Builder {
-    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
-    method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+    ctor public deprecated ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
+    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int, java.lang.CharSequence);
+    method public deprecated android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+    method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int, java.lang.CharSequence);
     method public android.service.autofill.ImageTransformation build();
   }
 
@@ -37226,6 +37228,9 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
   }
 
+  public abstract interface Sanitizer {
+  }
+
   public final class SaveCallback {
     method public void onFailure(java.lang.CharSequence);
     method public void onSuccess();
@@ -37249,6 +37254,7 @@
   public static final class SaveInfo.Builder {
     ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
     ctor public SaveInfo.Builder(int);
+    method public android.service.autofill.SaveInfo.Builder addSanitizer(android.service.autofill.Sanitizer, android.view.autofill.AutofillId...);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
@@ -37267,6 +37273,13 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
+  public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+    ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.TextValueSanitizer> CREATOR;
+  }
+
   public abstract interface Transformation {
   }
 
@@ -37606,8 +37619,12 @@
     method public java.lang.String getOverrideGroupKey();
     method public int getRank();
     method public int getSuppressedVisualEffects();
+    method public int getUserSentiment();
     method public boolean isAmbient();
     method public boolean matchesInterruptionFilter();
+    field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
+    field public static final int USER_SENTIMENT_NEUTRAL = 0; // 0x0
+    field public static final int USER_SENTIMENT_POSITIVE = 1; // 0x1
   }
 
   public static class NotificationListenerService.RankingMap implements android.os.Parcelable {
@@ -40685,11 +40702,10 @@
   }
 
   public static class DownloadRequest.Builder {
-    ctor public DownloadRequest.Builder();
+    ctor public DownloadRequest.Builder(android.net.Uri);
     method public android.telephony.mbms.DownloadRequest build();
     method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent);
     method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo);
-    method public android.telephony.mbms.DownloadRequest.Builder setSource(android.net.Uri);
     method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index bfb7a3a..ea077fe 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -40303,8 +40303,10 @@
   }
 
   public static class ImageTransformation.Builder {
-    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
-    method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+    ctor public deprecated ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
+    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int, java.lang.CharSequence);
+    method public deprecated android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+    method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int, java.lang.CharSequence);
     method public android.service.autofill.ImageTransformation build();
   }
 
@@ -40322,6 +40324,9 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
   }
 
+  public abstract interface Sanitizer {
+  }
+
   public final class SaveCallback {
     method public void onFailure(java.lang.CharSequence);
     method public void onSuccess();
@@ -40345,6 +40350,7 @@
   public static final class SaveInfo.Builder {
     ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
     ctor public SaveInfo.Builder(int);
+    method public android.service.autofill.SaveInfo.Builder addSanitizer(android.service.autofill.Sanitizer, android.view.autofill.AutofillId...);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
@@ -40363,6 +40369,13 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
+  public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+    ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.TextValueSanitizer> CREATOR;
+  }
+
   public abstract interface Transformation {
   }
 
@@ -40590,6 +40603,7 @@
     field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
     field public static final java.lang.String KEY_PEOPLE = "key_people";
     field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
+    field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
   }
 
   public final class Condition implements android.os.Parcelable {
@@ -40644,6 +40658,7 @@
     method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
     method public final android.os.IBinder onBind(android.content.Intent);
     method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
+    method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
     method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
     method public final void unsnoozeNotification(java.lang.String);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
@@ -40728,8 +40743,12 @@
     method public java.lang.String getOverrideGroupKey();
     method public int getRank();
     method public int getSuppressedVisualEffects();
+    method public int getUserSentiment();
     method public boolean isAmbient();
     method public boolean matchesInterruptionFilter();
+    field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
+    field public static final int USER_SENTIMENT_NEUTRAL = 0; // 0x0
+    field public static final int USER_SENTIMENT_POSITIVE = 1; // 0x1
   }
 
   public static class NotificationListenerService.RankingMap implements android.os.Parcelable {
@@ -40740,6 +40759,32 @@
     field public static final android.os.Parcelable.Creator<android.service.notification.NotificationListenerService.RankingMap> CREATOR;
   }
 
+  public final class NotificationStats implements android.os.Parcelable {
+    ctor public NotificationStats();
+    ctor protected NotificationStats(android.os.Parcel);
+    method public int describeContents();
+    method public int getDismissalSurface();
+    method public boolean hasDirectReplied();
+    method public boolean hasExpanded();
+    method public boolean hasInteracted();
+    method public boolean hasSeen();
+    method public boolean hasSnoozed();
+    method public boolean hasViewedSettings();
+    method public void setDirectReplied();
+    method public void setDismissalSurface(int);
+    method public void setExpanded();
+    method public void setSeen();
+    method public void setSnoozed();
+    method public void setViewedSettings();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.notification.NotificationStats> CREATOR;
+    field public static final int DISMISSAL_AOD = 2; // 0x2
+    field public static final int DISMISSAL_NOT_DISMISSED = -1; // 0xffffffff
+    field public static final int DISMISSAL_OTHER = 0; // 0x0
+    field public static final int DISMISSAL_PEEK = 1; // 0x1
+    field public static final int DISMISSAL_SHADE = 3; // 0x3
+  }
+
   public final class SnoozeCriterion implements android.os.Parcelable {
     ctor public SnoozeCriterion(java.lang.String, java.lang.CharSequence, java.lang.CharSequence);
     ctor protected SnoozeCriterion(android.os.Parcel);
@@ -44286,13 +44331,12 @@
   }
 
   public static class DownloadRequest.Builder {
-    ctor public DownloadRequest.Builder();
+    ctor public DownloadRequest.Builder(android.net.Uri);
     method public android.telephony.mbms.DownloadRequest build();
     method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent);
     method public android.telephony.mbms.DownloadRequest.Builder setOpaqueData(byte[]);
     method public android.telephony.mbms.DownloadRequest.Builder setServiceId(java.lang.String);
     method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo);
-    method public android.telephony.mbms.DownloadRequest.Builder setSource(android.net.Uri);
     method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 560cfa8..fe861e1 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3866,7 +3866,9 @@
     method public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
     method public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException;
     method public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException;
+    method public void resizeStack(int, android.graphics.Rect) throws java.lang.SecurityException;
     method public deprecated void restartPackage(java.lang.String);
+    method public void setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
     method public static void setVrThread(int);
     method public void setWatchHeapLimit(long);
     method public static boolean supportsMultiWindow(android.content.Context);
@@ -4019,11 +4021,7 @@
   }
 
   public static class ActivityManager.StackId {
-    field public static final int DOCKED_STACK_ID = 3; // 0x3
-    field public static final int FREEFORM_WORKSPACE_STACK_ID = 2; // 0x2
-    field public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1; // 0x1
     field public static final int INVALID_STACK_ID = -1; // 0xffffffff
-    field public static final int PINNED_STACK_ID = 4; // 0x4
   }
 
   public static class ActivityManager.TaskDescription implements android.os.Parcelable {
@@ -37497,11 +37495,17 @@
   }
 
   public static class ImageTransformation.Builder {
-    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
-    method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+    ctor public deprecated ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
+    ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int, java.lang.CharSequence);
+    method public deprecated android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+    method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int, java.lang.CharSequence);
     method public android.service.autofill.ImageTransformation build();
   }
 
+  public abstract class InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+    ctor public InternalSanitizer();
+  }
+
   public final class LuhnChecksumValidator implements android.os.Parcelable android.service.autofill.Validator {
     ctor public LuhnChecksumValidator(android.view.autofill.AutofillId...);
     method public int describeContents();
@@ -37518,6 +37522,9 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
   }
 
+  public abstract interface Sanitizer {
+  }
+
   public final class SaveCallback {
     method public void onFailure(java.lang.CharSequence);
     method public void onSuccess();
@@ -37541,6 +37548,7 @@
   public static final class SaveInfo.Builder {
     ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
     ctor public SaveInfo.Builder(int);
+    method public android.service.autofill.SaveInfo.Builder addSanitizer(android.service.autofill.Sanitizer, android.view.autofill.AutofillId...);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
@@ -37559,6 +37567,14 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
+  public final class TextValueSanitizer extends android.service.autofill.InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+    ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
+    method public int describeContents();
+    method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.TextValueSanitizer> CREATOR;
+  }
+
   public abstract interface Transformation {
   }
 
@@ -37790,6 +37806,7 @@
     field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
     field public static final java.lang.String KEY_PEOPLE = "key_people";
     field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
+    field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
   }
 
   public final class Condition implements android.os.Parcelable {
@@ -37876,6 +37893,7 @@
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification);
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
     method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, int);
+    method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
     method public final void requestInterruptionFilter(int);
     method public final void requestListenerHints(int);
     method public static void requestRebind(android.content.ComponentName);
@@ -37928,8 +37946,12 @@
     method public java.lang.String getOverrideGroupKey();
     method public int getRank();
     method public int getSuppressedVisualEffects();
+    method public int getUserSentiment();
     method public boolean isAmbient();
     method public boolean matchesInterruptionFilter();
+    field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
+    field public static final int USER_SENTIMENT_NEUTRAL = 0; // 0x0
+    field public static final int USER_SENTIMENT_POSITIVE = 1; // 0x1
   }
 
   public static class NotificationListenerService.RankingMap implements android.os.Parcelable {
@@ -37940,6 +37962,32 @@
     field public static final android.os.Parcelable.Creator<android.service.notification.NotificationListenerService.RankingMap> CREATOR;
   }
 
+  public final class NotificationStats implements android.os.Parcelable {
+    ctor public NotificationStats();
+    ctor protected NotificationStats(android.os.Parcel);
+    method public int describeContents();
+    method public int getDismissalSurface();
+    method public boolean hasDirectReplied();
+    method public boolean hasExpanded();
+    method public boolean hasInteracted();
+    method public boolean hasSeen();
+    method public boolean hasSnoozed();
+    method public boolean hasViewedSettings();
+    method public void setDirectReplied();
+    method public void setDismissalSurface(int);
+    method public void setExpanded();
+    method public void setSeen();
+    method public void setSnoozed();
+    method public void setViewedSettings();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.notification.NotificationStats> CREATOR;
+    field public static final int DISMISSAL_AOD = 2; // 0x2
+    field public static final int DISMISSAL_NOT_DISMISSED = -1; // 0xffffffff
+    field public static final int DISMISSAL_OTHER = 0; // 0x0
+    field public static final int DISMISSAL_PEEK = 1; // 0x1
+    field public static final int DISMISSAL_SHADE = 3; // 0x3
+  }
+
   public final class SnoozeCriterion implements android.os.Parcelable {
     ctor public SnoozeCriterion(java.lang.String, java.lang.CharSequence, java.lang.CharSequence);
     ctor protected SnoozeCriterion(android.os.Parcel);
@@ -41035,11 +41083,10 @@
   }
 
   public static class DownloadRequest.Builder {
-    ctor public DownloadRequest.Builder();
+    ctor public DownloadRequest.Builder(android.net.Uri);
     method public android.telephony.mbms.DownloadRequest build();
     method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent);
     method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo);
-    method public android.telephony.mbms.DownloadRequest.Builder setSource(android.net.Uri);
     method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
   }
 
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 56f9512..ca36d27 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -44,7 +44,6 @@
     ../../core/java/android/os/IStatsManager.aidl \
     src/StatsService.cpp \
     src/AnomalyMonitor.cpp \
-    src/StatsPuller.cpp \
     src/LogEntryPrinter.cpp \
     src/LogReader.cpp \
     src/main.cpp \
@@ -53,12 +52,14 @@
     src/StatsLogProcessor.cpp \
     src/stats_log.proto \
     src/statsd_config.proto \
+    src/StatsPullerManager.cpp \
+    src/KernelWakelockPuller.cpp \
     src/DropboxReader.cpp \
     src/matchers/LogEntryMatcherManager.cpp \
     src/metrics/CountMetricProducer.cpp \
     src/metrics/ConditionTracker.cpp \
     src/metrics/MetricsManager.cpp \
-
+    src/metrics/CountAnomalyTracker.cpp \
 
 LOCAL_CFLAGS += \
     -Wall \
diff --git a/cmds/statsd/src/KernelWakelockPuller.cpp b/cmds/statsd/src/KernelWakelockPuller.cpp
new file mode 100644
index 0000000..1798f9d
--- /dev/null
+++ b/cmds/statsd/src/KernelWakelockPuller.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#include "KernelWakelockPuller.h"
+#include <android/os/IStatsCompanionService.h>
+#include <binder/IPCThreadState.h>
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+#include "StatsPuller.h"
+#include "StatsService.h"
+
+using namespace android;
+using namespace android::base;
+using namespace android::binder;
+using namespace android::os;
+using namespace std;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+const int KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS = 20;
+
+// The reading and parsing are implemented in Java. It is not difficult to port over. But for now
+// let StatsCompanionService handle that and send the data back.
+String16 KernelWakelockPuller::pull() {
+    sp<IStatsCompanionService> statsCompanion = StatsService::getStatsCompanionService();
+    String16 returned_value("");
+    if (statsCompanion != NULL) {
+      Status status = statsCompanion->pullData(KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS,
+                                             &returned_value);
+      if (!status.isOk()) {
+          ALOGW("error pulling kernel wakelock");
+      }
+      ALOGD("KernelWakelockPuller::pull succeeded!");
+      // TODO: remove this when we integrate into aggregation chain.
+      ALOGD("%s", String8(returned_value).string());
+      return returned_value;
+    } else {
+        ALOGW("statsCompanion not found!");
+        return String16();
+    }
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/KernelWakelockPuller.h b/cmds/statsd/src/KernelWakelockPuller.h
new file mode 100644
index 0000000..1c16f87
--- /dev/null
+++ b/cmds/statsd/src/KernelWakelockPuller.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef STATSD_KERNELWAKELOCKPULLER_H
+#define STATSD_KERNELWAKELOCKPULLER_H
+
+#include <utils/String16.h>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class KernelWakelockPuller : public StatsPuller {
+public:
+    // a number of stats need to be pulled from StatsCompanionService
+    //
+    const static int PULL_CODE_KERNEL_WAKELOCKS;
+    String16 pull() override;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+
+#endif  // STATSD_KERNELWAKELOCKPULLER_H
diff --git a/cmds/statsd/src/StatsPuller.cpp b/cmds/statsd/src/StatsPuller.cpp
deleted file mode 100644
index 94e8361..0000000
--- a/cmds/statsd/src/StatsPuller.cpp
+++ /dev/null
@@ -1,60 +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.
- */
-
-#define LOG_TAG "StatsPuller"
-#define DEBUG true
-
-#include "StatsPuller.h"
-#include "StatsService.h"
-#include <android/os/IStatsCompanionService.h>
-#include <cutils/log.h>
-
-using namespace android;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-String16 StatsPuller::pull(int pullCode) {
-    if (DEBUG) ALOGD("Initiating pulling %d", pullCode);
-
-    switch (pullCode) {
-        // All stats_companion_service cases go here with fallthroughs
-        case PULL_CODE_KERNEL_WAKELOCKS: {
-            // TODO: Consider caching the statsCompanion service
-            sp <IStatsCompanionService>
-                    statsCompanion = StatsService::getStatsCompanionService();
-            String16 returned_value("");
-            Status status = statsCompanion->pullData(pullCode, &returned_value);
-            if (DEBUG) ALOGD("Finished pulling the data");
-            if (!status.isOk()) {
-                ALOGW("error pulling data of type %d", pullCode);
-            }
-            return returned_value;
-        }
-
-        // case OTHER_TYPES: etc.
-
-        default: {
-            ALOGE("invalid pull code %d", pullCode);
-            return String16("");
-        }
-    }
-}
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/cmds/statsd/src/StatsPuller.h b/cmds/statsd/src/StatsPuller.h
index 05343b5..5e556b8 100644
--- a/cmds/statsd/src/StatsPuller.h
+++ b/cmds/statsd/src/StatsPuller.h
@@ -25,19 +25,13 @@
 
 class StatsPuller {
 public:
-    // Enums of pulled data types (pullCodes)
-    // These values must be kept in sync with com/android/server/stats/StatsCompanionService.java.
-    // TODO: pull the constant from stats_events.proto instead
-    const static int PULL_CODE_KERNEL_WAKELOCKS = 20;
-
-    StatsPuller();
-    ~StatsPuller();
-
-    static String16 pull(int pullCode);
+    virtual ~StatsPuller(){};
+    // use string for now, until we figure out how to integrate into the aggregation path
+    virtual String16 pull() = 0;
 };
 
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
 
-#endif //STATSD_STATSPULLER_H
+#endif  // STATSD_STATSPULLER_H
diff --git a/cmds/statsd/src/StatsPullerManager.cpp b/cmds/statsd/src/StatsPullerManager.cpp
new file mode 100644
index 0000000..f4cf1ce
--- /dev/null
+++ b/cmds/statsd/src/StatsPullerManager.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "StatsPullerManager"
+#define DEBUG true
+
+#include "StatsPullerManager.h"
+#include <android/os/IStatsCompanionService.h>
+#include <cutils/log.h>
+#include "StatsService.h"
+#include "KernelWakelockPuller.h"
+
+
+using namespace android;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+const int StatsPullerManager::KERNEL_WAKELOCKS = 1;
+
+StatsPullerManager::StatsPullerManager() {
+    mStatsPullers.insert(
+            {static_cast<int>(KERNEL_WAKELOCKS), std::make_unique<KernelWakelockPuller>()});
+}
+
+String16 StatsPullerManager::pull(int pullCode) {
+    if (DEBUG) ALOGD("Initiating pulling %d", pullCode);
+    if (mStatsPullers.find(pullCode) != mStatsPullers.end()) {
+        return (mStatsPullers.find(pullCode)->second)->pull();
+    } else {
+        ALOGD("Unknown pull code %d", pullCode);
+        return String16();
+    }
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/StatsPullerManager.h b/cmds/statsd/src/StatsPullerManager.h
new file mode 100644
index 0000000..ab36df5
--- /dev/null
+++ b/cmds/statsd/src/StatsPullerManager.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef STATSD_STATSPULLERMANAGER_H
+#define STATSD_STATSPULLERMANAGER_H
+
+#include <utils/String16.h>
+#include <unordered_map>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+const static int KERNEL_WAKELOCKS = 1;
+
+class StatsPullerManager {
+public:
+    // Enums of pulled data types (pullCodes)
+    // These values must be kept in sync with com/android/server/stats/StatsCompanionService.java.
+    // TODO: pull the constant from stats_events.proto instead
+    const static int KERNEL_WAKELOCKS;
+    StatsPullerManager();
+
+    String16 pull(const int pullCode);
+
+private:
+    std::unordered_map<int, std::unique_ptr<StatsPuller>> mStatsPullers;
+};
+
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+
+#endif  // STATSD_STATSPULLERMANAGER_H
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index ae7d66b..9baeebb 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -40,7 +40,7 @@
 namespace statsd {
 
 StatsService::StatsService(const sp<Looper>& handlerLooper)
-    : mAnomalyMonitor(new AnomalyMonitor(2))  // TODO: Change this based on the config
+    : mAnomalyMonitor(new AnomalyMonitor(2)), mStatsPullerManager()  // TODO: Change this based on the config
 {
     ALOGD("stats service constructed");
 }
@@ -177,9 +177,10 @@
 
     if (DEBUG) ALOGD("StatsService::informPollAlarmFired succeeded");
     // TODO: determine what services to poll and poll (or ask StatsCompanionService to poll) them.
-    String16 output = StatsPuller::pull(StatsPuller::PULL_CODE_KERNEL_WAKELOCKS);
+    String16 output = mStatsPullerManager.pull(StatsPullerManager::KERNEL_WAKELOCKS);
     // TODO: do something useful with the output instead of writing a string to screen.
     ALOGD("%s", String8(output).string());
+    ALOGD("%d", int(output.size()));
 
     return Status::ok();
 }
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index a16b115..9642279 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -19,7 +19,7 @@
 
 #include "AnomalyMonitor.h"
 #include "StatsLogProcessor.h"
-#include "StatsPuller.h"
+#include "StatsPullerManager.h"
 
 #include <android/os/BnStatsManager.h>
 #include <android/os/IStatsCompanionService.h>
@@ -74,7 +74,7 @@
     /** Fetches and returns the StatsCompanionService. */
     static sp<IStatsCompanionService> getStatsCompanionService();
 
- private:
+private:
     sp<StatsLogProcessor> m_processor;  // Reference to the processor for updating configs.
 
     status_t doPrintStatsLog(FILE* out, const Vector<String8>& args);
@@ -82,6 +82,8 @@
     void printCmdHelp(FILE* out);
 
     status_t doLoadConfig(FILE* in);
+
+    StatsPullerManager mStatsPullerManager;
 };
 
 // --- StatsdDeathRecipient ---
diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp b/cmds/statsd/src/metrics/CountAnomalyTracker.cpp
new file mode 100644
index 0000000..ebd53e0
--- /dev/null
+++ b/cmds/statsd/src/metrics/CountAnomalyTracker.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "CountAnomaly"
+#define DEBUG true  // STOPSHIP if true
+#define VLOG(...) \
+    if (DEBUG) ALOGD(__VA_ARGS__);
+
+#include "CountAnomalyTracker.h"
+
+#include <cutils/log.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+CountAnomalyTracker::CountAnomalyTracker(size_t numBuckets, int thresholdGt)
+    : mNumPastBuckets(numBuckets > 0 ? numBuckets - 1 : 0),
+      mPastBuckets(mNumPastBuckets > 0 ? (new int[mNumPastBuckets]) : nullptr),
+      mThresholdGt(thresholdGt) {
+
+    VLOG("CountAnomalyTracker() called");
+    if (numBuckets < 1) {
+        ALOGE("Cannot create CountAnomalyTracker with %zu buckets", numBuckets);
+    }
+    reset(); // initialization
+}
+
+CountAnomalyTracker::~CountAnomalyTracker() {
+    VLOG("~CountAnomalyTracker() called");
+}
+
+void CountAnomalyTracker::addPastBucket(int pastBucketCount,
+                                        time_t numberOfBucketsAgo) {
+    VLOG("addPastBucket() called.");
+    if (numberOfBucketsAgo < 1) {
+        ALOGE("Cannot add a past bucket %ld units in past", numberOfBucketsAgo);
+        return;
+    }
+    // If past bucket was ancient, just empty out all past info.
+    // This always applies if mNumPastBuckets == 0 (i.e. store no past buckets).
+    if (numberOfBucketsAgo > (time_t) mNumPastBuckets) {
+        reset();
+        return;
+    }
+
+    // Empty out old mPastBuckets[i] values and update mSumPastCounters.
+    for (size_t i = mOldestBucketIndex;
+                        i < mOldestBucketIndex + numberOfBucketsAgo; i++) {
+        mSumPastCounters -= mPastBuckets[index(i)];
+        mPastBuckets[index(i)] = 0;
+    }
+
+    // Replace the oldest bucket with the new bucket we are adding.
+    mPastBuckets[mOldestBucketIndex] = pastBucketCount;
+    mSumPastCounters += pastBucketCount;
+
+    // Advance the oldest bucket index by numberOfBucketsAgo units.
+    mOldestBucketIndex = index(mOldestBucketIndex + numberOfBucketsAgo);
+
+    // TODO: Once dimensions are added to mSumPastCounters:
+    // iterate through mSumPastCounters and remove any entries that are 0.
+}
+
+void CountAnomalyTracker::reset() {
+    VLOG("reset() called.");
+    for (size_t i = 0; i < mNumPastBuckets; i++) {
+        mPastBuckets[i] = 0;
+    }
+    mSumPastCounters = 0;
+    mOldestBucketIndex = 0;
+}
+
+void CountAnomalyTracker::checkAnomaly(int currentCount) {
+    // Note that this works even if mNumPastBuckets < 1 (since then
+    // mSumPastCounters = 0 so the comparison is based only on currentCount).
+
+    // TODO: Remove these extremely verbose debugging log.
+    VLOG("Checking whether %d + %d > %d",
+         mSumPastCounters, currentCount, mThresholdGt);
+
+    if (mSumPastCounters + currentCount > mThresholdGt) {
+        declareAnomaly();
+    }
+}
+
+void CountAnomalyTracker::declareAnomaly() {
+    // TODO: check that not in refractory period.
+    // TODO: Do something.
+    ALOGI("An anomaly has occurred!");
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.h b/cmds/statsd/src/metrics/CountAnomalyTracker.h
new file mode 100644
index 0000000..449dee9
--- /dev/null
+++ b/cmds/statsd/src/metrics/CountAnomalyTracker.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#ifndef COUNT_ANOMALY_TRACKER_H
+#define COUNT_ANOMALY_TRACKER_H
+
+#include <stdlib.h>
+#include <memory> // unique_ptr
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class CountAnomalyTracker {
+public:
+    CountAnomalyTracker(size_t numBuckets, int thresholdGt);
+
+    virtual ~CountAnomalyTracker();
+
+
+    // Adds a new past bucket, holding pastBucketCount, and then advances the
+    // present by numberOfBucketsAgo buckets (filling any intervening buckets
+    // with 0s).
+    // Thus, the newly added bucket (which holds pastBucketCount) is stored
+    // numberOfBucketsAgo buckets ago.
+    void addPastBucket(int pastBucketCount, time_t numberOfBucketsAgo);
+
+    // Informs the anomaly tracker of the current bucket's count, so that it can
+    // determine whether an anomaly has occurred. This value is not stored.
+    void checkAnomaly(int currentCount);
+
+private:
+    // Number of past buckets. One less than the total number of buckets needed
+    // for the anomaly detection (since the current bucket is not in the past).
+    const size_t mNumPastBuckets;
+
+    // Count values for each of the past mNumPastBuckets buckets.
+    // TODO: Add dimensions. This parallels the type of CountMetricProducer.mCounter.
+    std::unique_ptr<int[]> mPastBuckets;
+
+    // Sum over all of mPastBuckets (cached).
+    // TODO: Add dimensions. This parallels the type of CountMetricProducer.mCounter.
+    //       At that point, mSumPastCounters must never contain entries of 0.
+    int mSumPastCounters;
+
+    // Index of the oldest bucket (i.e. the next bucket to be overwritten).
+    size_t mOldestBucketIndex = 0;
+
+    // If mSumPastCounters + currentCount > mThresholdGt --> Anomaly!
+    const int mThresholdGt;
+
+    void declareAnomaly();
+
+    // Calculates the corresponding index within the circular array.
+    size_t index(size_t unsafeIndex) {
+        return unsafeIndex % mNumPastBuckets;
+    }
+
+    // Resets all data. For use when all the data gets stale.
+    void reset();
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#endif  // COUNT_ANOMALY_TRACKER_H
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index fbd013e..635777f 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -20,6 +20,7 @@
     if (DEBUG) ALOGD(__VA_ARGS__);
 
 #include "CountMetricProducer.h"
+#include "CountAnomalyTracker.h"
 #include "parse_util.h"
 
 #include <cutils/log.h>
@@ -38,7 +39,9 @@
       mConditionTracker(condition),
       mStartTime(std::time(nullptr)),
       mCounter(0),
-      mCurrentBucketStartTime(mStartTime) {
+      mCurrentBucketStartTime(mStartTime),
+      // TODO: read mAnomalyTracker parameters from config file.
+      mAnomalyTracker(6, 10) {
     // TODO: evaluate initial conditions. and set mConditionMet.
     if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
         mBucketSize_sec = metric.bucket().bucket_size_millis() / 1000;
@@ -78,6 +81,7 @@
     if (mConditionTracker->isConditionMet()) {
         flushCounterIfNeeded(eventTime);
         mCounter++;
+        mAnomalyTracker.checkAnomaly(mCounter);
     }
 }
 
@@ -91,13 +95,16 @@
     // TODO: add a KeyValuePair to StatsLogReport.
     ALOGD("CountMetric: dump counter %d", mCounter);
 
-    // reset counter
-    mCounter = 0;
-
     // adjust the bucket start time
-    mCurrentBucketStartTime =
-            mCurrentBucketStartTime +
-            ((eventTime - mCurrentBucketStartTime) / mBucketSize_sec) * mBucketSize_sec;
+    time_t numBucketsForward = (eventTime - mCurrentBucketStartTime)
+            / mBucketSize_sec;
+
+    mCurrentBucketStartTime = mCurrentBucketStartTime +
+            (numBucketsForward) * mBucketSize_sec;
+
+    // reset counter
+    mAnomalyTracker.addPastBucket(mCounter, numBucketsForward);
+    mCounter = 0;
 
     VLOG("new bucket start time: %lu", mCurrentBucketStartTime);
 }
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 7665791..7502320 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -21,6 +21,7 @@
 #include <thread>
 #include <unordered_map>
 #include "../matchers/LogEntryMatcherManager.h"
+#include "CountAnomalyTracker.h"
 #include "ConditionTracker.h"
 #include "DropboxWriter.h"
 #include "MetricProducer.h"
@@ -52,12 +53,15 @@
 
     const time_t mStartTime;
     // TODO: Add dimensions.
+    // Counter value for the current bucket.
     int mCounter;
 
     time_t mCurrentBucketStartTime;
 
     long mBucketSize_sec;
 
+    CountAnomalyTracker mAnomalyTracker;
+
     void flushCounterIfNeeded(const time_t& newEventTime);
 };
 
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e0ac911..d988a42 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -542,9 +542,9 @@
  * <ul>
  *     <li> <p>When creating a new document, the backing database entry or file for
  *             it is created immediately.  For example, if the user chooses to write
- *             a new e-mail, a new entry for that e-mail is created as soon as they
+ *             a new email, a new entry for that email is created as soon as they
  *             start entering data, so that if they go to any other activity after
- *             that point this e-mail will now appear in the list of drafts.</p>
+ *             that point this email will now appear in the list of drafts.</p>
  *     <li> <p>When an activity's <code>onPause()</code> method is called, it should
  *             commit to the backing content provider or file any changes the user
  *             has made.  This ensures that those changes will be seen by any other
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 26f96fb..5e61727 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,10 +16,10 @@
 
 package android.app;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
@@ -674,16 +674,20 @@
          * @hide */
         private  static final int FIRST_STATIC_STACK_ID = 0;
 
-        /** ID of stack where fullscreen activities are normally launched into. */
+        /** ID of stack where fullscreen activities are normally launched into.
+         * @hide */
         public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
 
-        /** ID of stack where freeform/resized activities are normally launched into. */
+        /** ID of stack where freeform/resized activities are normally launched into.
+         * @hide */
         public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
 
-        /** ID of stack that occupies a dedicated region of the screen. */
+        /** ID of stack that occupies a dedicated region of the screen.
+         * @hide */
         public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
 
-        /** ID of stack that always on top (always visible) when it exist. */
+        /** ID of stack that always on top (always visible) when it exist.
+         * @hide */
         public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
 
         /** Last static stack stack ID.
@@ -1531,6 +1535,12 @@
          */
         public int resizeMode;
 
+        /**
+         * The current configuration this task is in.
+         * @hide
+         */
+        final public Configuration configuration = new Configuration();
+
         public RecentTaskInfo() {
         }
 
@@ -1576,6 +1586,7 @@
             }
             dest.writeInt(supportsSplitScreenMultiWindow ? 1 : 0);
             dest.writeInt(resizeMode);
+            configuration.writeToParcel(dest, flags);
         }
 
         public void readFromParcel(Parcel source) {
@@ -1600,6 +1611,7 @@
                     Rect.CREATOR.createFromParcel(source) : null;
             supportsSplitScreenMultiWindow = source.readInt() == 1;
             resizeMode = source.readInt();
+            configuration.readFromParcel(source);
         }
 
         public static final Creator<RecentTaskInfo> CREATOR
@@ -1798,7 +1810,7 @@
          * The full configuration the task is currently running in.
          * @hide
          */
-        public Configuration configuration = new Configuration();
+        final public Configuration configuration = new Configuration();
 
         public RunningTaskInfo() {
         }
@@ -2025,12 +2037,49 @@
     }
 
     /**
+     * Sets the windowing mode for a specific task. Only works on tasks of type
+     * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}
+     * @param taskId The id of the task to set the windowing mode for.
+     * @param windowingMode The windowing mode to set for the task.
+     * @param toTop If the task should be moved to the top once the windowing mode changes.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+    public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop)
+            throws SecurityException {
+        try {
+            getService().setTaskWindowingMode(taskId, windowingMode, toTop);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Resizes the input stack id to the given bounds.
+     * @param stackId Id of the stack to resize.
+     * @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+    public void resizeStack(int stackId, Rect bounds) throws SecurityException {
+        try {
+            getService().resizeStack(stackId, bounds, false /* allowResizeInDockedMode */,
+                    false /* preserveWindows */, false /* animate */, -1 /* animationDuration */);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Removes stacks in the windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      *
      * @hide
      */
     @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
     public void removeStacksInWindowingModes(int[] windowingModes) throws SecurityException {
         try {
             getService().removeStacksInWindowingModes(windowingModes);
@@ -2045,6 +2094,7 @@
      * @hide
      */
     @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
     public void removeStacksWithActivityTypes(int[] activityTypes) throws SecurityException {
         try {
             getService().removeStacksWithActivityTypes(activityTypes);
@@ -2535,7 +2585,7 @@
          * The full configuration the stack is currently running in.
          * @hide
          */
-        public Configuration configuration = new Configuration();
+        final public Configuration configuration = new Configuration();
 
         @Override
         public int describeContents() {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 955b463..f039516 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -361,6 +361,15 @@
     void killUid(int appId, int userId, in String reason);
     void setUserIsMonkey(boolean monkey);
     void hang(in IBinder who, boolean allowRestart);
+
+    /**
+     * Sets the windowing mode for a specific task. Only works on tasks of type
+     * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}
+     * @param taskId The id of the task to set the windowing mode for.
+     * @param windowingMode The windowing mode to set for the task.
+     * @param toTop If the task should be moved to the top once the windowing mode changes.
+     */
+    void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop);
     void moveTaskToStack(int taskId, int stackId, boolean toTop);
     /**
      * Resizes the input stack id to the given bounds.
@@ -490,6 +499,18 @@
             in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
     boolean moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
             in Rect initialBounds);
+    /**
+     * Dismisses split-screen multi-window mode.
+     * {@param toTop} If true the current primary split-screen stack will be placed or left on top.
+     */
+    void dismissSplitScreenMode(boolean toTop);
+    /**
+     * Dismisses PiP
+     * @param animate True if the dismissal should be animated.
+     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
+     *                          default animation duration should be used.
+     */
+    void dismissPip(boolean animate, int animationDuration);
     void suppressResizeConfigChanges(boolean suppress);
     void moveTasksToFullscreenStack(int fromStackId, boolean onTop);
     boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds);
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index a56965b..2e1e988 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -30,7 +30,7 @@
     void onTaskStackChanged();
 
     /** Called whenever an Activity is moved to the pinned stack from another stack. */
-    void onActivityPinned(String packageName, int userId, int taskId);
+    void onActivityPinned(String packageName, int userId, int taskId, int stackId);
 
     /** Called whenever an Activity is moved from the pinned stack to another stack. */
     void onActivityUnpinned();
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 4674c9c..402e209 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -31,7 +31,7 @@
     }
 
     @Override
-    public void onActivityPinned(String packageName, int userId, int taskId)
+    public void onActivityPinned(String packageName, int userId, int taskId, int stackId)
             throws RemoteException {
     }
 
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index cdeaea3..5b2bf45 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -2099,7 +2099,8 @@
     public static Uri maybeAddUserId(Uri uri, int userId) {
         if (uri == null) return null;
         if (userId != UserHandle.USER_CURRENT
-                && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+                && (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
+                        || ContentResolver.SCHEME_SLICE.equals(uri.getScheme()))) {
             if (!uriHasUserId(uri)) {
                 //We don't add the user Id if there's already one
                 Uri.Builder builder = uri.buildUpon();
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index be7f921..143c51d 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -467,6 +467,7 @@
     /** Updates the flags for the given permission. */
     public abstract void updatePermissionFlagsTEMP(@NonNull String permName,
             @NonNull String packageName, int flagMask, int flagValues, int userId);
-    /** temporary until mPermissionTrees is moved to PermissionManager */
-    public abstract Object enforcePermissionTreeTEMP(@NonNull String permName, int callingUid);
+    /** Returns a PermissionGroup. */
+    public abstract @Nullable PackageParser.PermissionGroup getPermissionGroupTEMP(
+            @NonNull String groupName);
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 6c7c8a07..ec48ac5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -6849,6 +6849,11 @@
             dest.writeParcelable(group, flags);
         }
 
+        /** @hide */
+        public boolean isAppOp() {
+            return info.isAppOp();
+        }
+
         private Permission(Parcel in) {
             super(in);
             final ClassLoader boot = Object.class.getClassLoader();
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index b45c26c..5dd7aed 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -353,6 +353,11 @@
         return size;
     }
 
+    /** @hide */
+    public boolean isAppOp() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
+    }
+
     public static final Creator<PermissionInfo> CREATOR =
         new Creator<PermissionInfo>() {
         @Override
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 6fbacaf..b2af44e 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -278,6 +278,19 @@
      */
     public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 1 << 7;
 
+    /**
+     * Virtual display flag: Indicates that the contents will be destroyed once
+     * the display is removed.
+     *
+     * Public virtual displays without this flag will move their content to main display
+     * stack once they're removed. Private vistual displays will always destroy their
+     * content on removal even without this flag.
+     *
+     * @see #createVirtualDisplay
+     * @hide
+     */
+    public static final int VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 8;
+
     /** @hide */
     public DisplayManager(Context context) {
         mContext = context;
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 2c9fb23..4e474c8 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -683,9 +683,9 @@
      */
     public boolean hasIPv4Address() {
         for (LinkAddress address : mLinkAddresses) {
-          if (address.getAddress() instanceof Inet4Address) {
-            return true;
-          }
+            if (address.getAddress() instanceof Inet4Address) {
+                return true;
+            }
         }
         return false;
     }
@@ -725,9 +725,9 @@
      */
     public boolean hasIPv4DefaultRoute() {
         for (RouteInfo r : mRoutes) {
-          if (r.isIPv4Default()) {
-            return true;
-          }
+            if (r.isIPv4Default()) {
+                return true;
+            }
         }
         return false;
     }
@@ -740,9 +740,9 @@
      */
     public boolean hasIPv6DefaultRoute() {
         for (RouteInfo r : mRoutes) {
-          if (r.isIPv6Default()) {
-            return true;
-          }
+            if (r.isIPv6Default()) {
+                return true;
+            }
         }
         return false;
     }
@@ -755,9 +755,9 @@
      */
     public boolean hasIPv4DnsServer() {
         for (InetAddress ia : mDnses) {
-          if (ia instanceof Inet4Address) {
-            return true;
-          }
+            if (ia instanceof Inet4Address) {
+                return true;
+            }
         }
         return false;
     }
@@ -770,9 +770,9 @@
      */
     public boolean hasIPv6DnsServer() {
         for (InetAddress ia : mDnses) {
-          if (ia instanceof Inet6Address) {
-            return true;
-          }
+            if (ia instanceof Inet6Address) {
+                return true;
+            }
         }
         return false;
     }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index bf0a264..9881927 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -19,6 +19,7 @@
 import android.app.job.JobParameters;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.service.batterystats.BatteryStatsServiceDumpProto;
 import android.telephony.SignalStrength;
 import android.text.format.DateFormat;
 import android.util.ArrayMap;
@@ -29,11 +30,13 @@
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatteryStatsHelper;
 
+import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -190,7 +193,7 @@
     public static final int STATS_SINCE_UNPLUGGED = 2;
 
     // NOTE: Update this list if you add/change any stats above.
-    // These characters are supposed to represent "total", "last", "current", 
+    // These characters are supposed to represent "total", "last", "current",
     // and "unplugged". They were shortened for efficiency sake.
     private static final String[] STAT_NAMES = { "l", "c", "u" };
 
@@ -220,7 +223,7 @@
      * New in version 27:
      *   - Always On Display (screen doze mode) time and power
      */
-    static final String CHECKIN_VERSION = "27";
+    static final int CHECKIN_VERSION = 27;
 
     /**
      * Old version, we hit 9 and ran out of room, need to remove.
@@ -2988,6 +2991,31 @@
     }
 
     /**
+     * Dump a given timer stat to the proto stream.
+     *
+     * @param proto the ProtoOutputStream to log to
+     * @param fieldId type of data, the field to save to (e.g. AggregatedBatteryStats.WAKELOCK)
+     * @param timer a {@link Timer} to dump stats for
+     * @param rawRealtimeUs the current elapsed realtime of the system in microseconds
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
+     */
+    private static void dumpTimer(ProtoOutputStream proto, long fieldId,
+                                        Timer timer, long rawRealtime, int which) {
+        if (timer == null) {
+            return;
+        }
+        // Convert from microseconds to milliseconds with rounding
+        final long totalTimeMs = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+        final int count = timer.getCountLocked(which);
+        if (totalTimeMs != 0 || count != 0) {
+            final long token = proto.start(fieldId);
+            proto.write(TimerProto.DURATION_MS, totalTimeMs);
+            proto.write(TimerProto.COUNT, count);
+            proto.end(token);
+        }
+    }
+
+    /**
      * Checks if the ControllerActivityCounter has any data worth dumping.
      */
     private static boolean controllerActivityHasData(ControllerActivityCounter counter, int which) {
@@ -3039,6 +3067,38 @@
         pw.println();
     }
 
+    /**
+     * Dumps the ControllerActivityCounter if it has any data worth dumping.
+     */
+    private static void dumpControllerActivityProto(ProtoOutputStream proto, long fieldId,
+                                                    ControllerActivityCounter counter,
+                                                    int which) {
+        if (!controllerActivityHasData(counter, which)) {
+            return;
+        }
+
+        final long cToken = proto.start(fieldId);
+
+        proto.write(ControllerActivityProto.IDLE_DURATION_MS,
+                counter.getIdleTimeCounter().getCountLocked(which));
+        proto.write(ControllerActivityProto.RX_DURATION_MS,
+                counter.getRxTimeCounter().getCountLocked(which));
+        proto.write(ControllerActivityProto.POWER_MAH,
+                counter.getPowerCounter().getCountLocked(which) / (1000 * 60 * 60));
+
+        long tToken;
+        LongCounter[] txCounters = counter.getTxTimeCounters();
+        for (int i = 0; i < txCounters.length; ++i) {
+            LongCounter c = txCounters[i];
+            tToken = proto.start(ControllerActivityProto.TX);
+            proto.write(ControllerActivityProto.TxLevel.LEVEL, i);
+            proto.write(ControllerActivityProto.TxLevel.DURATION_MS, c.getCountLocked(which));
+            proto.end(tToken);
+        }
+
+        proto.end(cToken);
+    }
+
     private final void printControllerActivityIfInteresting(PrintWriter pw, StringBuilder sb,
                                                             String prefix, String controllerName,
                                                             ControllerActivityCounter counter,
@@ -5491,6 +5551,7 @@
     }
 
     public void prepareForDumpLocked() {
+        // We don't need to require subclasses implement this.
     }
 
     public static class HistoryPrinter {
@@ -6312,6 +6373,7 @@
         }
     }
 
+    // This is called from BatteryStatsService.
     @SuppressWarnings("unused")
     public void dumpCheckinLocked(Context context, PrintWriter pw,
             List<ApplicationInfo> apps, int flags, long histStart) {
@@ -6323,10 +6385,7 @@
 
         long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
 
-        final boolean filtering = (flags &
-                (DUMP_HISTORY_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0;
-
-        if ((flags&DUMP_INCLUDE_HISTORY) != 0 || (flags&DUMP_HISTORY_ONLY) != 0) {
+        if ((flags & (DUMP_INCLUDE_HISTORY | DUMP_HISTORY_ONLY)) != 0) {
             if (startIteratingHistoryLocked()) {
                 try {
                     for (int i=0; i<getHistoryStringPoolSize(); i++) {
@@ -6350,7 +6409,7 @@
             }
         }
 
-        if (filtering && (flags&(DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) == 0) {
+        if ((flags & DUMP_HISTORY_ONLY) != 0) {
             return;
         }
 
@@ -6383,7 +6442,7 @@
                 }
             }
         }
-        if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
+        if ((flags & DUMP_DAILY_ONLY) == 0) {
             dumpDurationSteps(pw, "", DISCHARGE_STEP_DATA, getDischargeLevelStepTracker(), true);
             String[] lineArgs = new String[1];
             long timeRemaining = computeBatteryTimeRemaining(SystemClock.elapsedRealtime() * 1000);
@@ -6403,4 +6462,33 @@
                     (flags&DUMP_DEVICE_WIFI_ONLY) != 0);
         }
     }
+
+    /** Dump batterystats data to a proto. @hide */
+    public void dumpProtoLocked(Context context, FileDescriptor fd, List<ApplicationInfo> apps,
+            int flags, long historyStart) {
+        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+        final long bToken = proto.start(BatteryStatsServiceDumpProto.BATTERYSTATS);
+        prepareForDumpLocked();
+
+        proto.write(BatteryStatsProto.REPORT_VERSION, CHECKIN_VERSION);
+        proto.write(BatteryStatsProto.PARCEL_VERSION, getParcelVersion());
+        proto.write(BatteryStatsProto.START_PLATFORM_VERSION, getStartPlatformVersion());
+        proto.write(BatteryStatsProto.END_PLATFORM_VERSION, getEndPlatformVersion());
+
+        long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
+
+        if ((flags & (DUMP_INCLUDE_HISTORY | DUMP_HISTORY_ONLY)) != 0) {
+            if (startIteratingHistoryLocked()) {
+                // TODO: implement dumpProtoHistoryLocked(proto);
+            }
+        }
+
+        if ((flags & (DUMP_HISTORY_ONLY | DUMP_DAILY_ONLY)) == 0) {
+            // TODO: implement dumpProtoAppsLocked(proto, apps);
+            // TODO: implement dumpProtoSystemLocked(proto);
+        }
+
+        proto.end(bToken);
+        proto.flush();
+    }
 }
diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl
index cc2af21..8271701 100644
--- a/core/java/android/os/IDeviceIdleController.aidl
+++ b/core/java/android/os/IDeviceIdleController.aidl
@@ -23,6 +23,11 @@
 interface IDeviceIdleController {
     void addPowerSaveWhitelistApp(String name);
     void removePowerSaveWhitelistApp(String name);
+    /* Removes an app from the system whitelist. Calling restoreSystemPowerWhitelistApp will add
+    the app back into the system whitelist */
+    void removeSystemPowerWhitelistApp(String name);
+    void restoreSystemPowerWhitelistApp(String name);
+    String[] getRemovedSystemPowerWhitelistApps();
     String[] getSystemPowerWhitelistExceptIdle();
     String[] getSystemPowerWhitelist();
     String[] getUserPowerWhitelist();
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index c091420..7f588ad 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -737,7 +737,9 @@
     private void closeWithStatus(int status, String msg) {
         if (mClosed) return;
         mClosed = true;
-        mGuard.close();
+        if (mGuard != null) {
+            mGuard.close();
+        }
         // Status MUST be sent before closing actual descriptor
         writeCommStatusAndClose(status, msg);
         IoUtils.closeQuietly(mFd);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index eca2fcd..a062db4 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9369,16 +9369,6 @@
         public static final String DEVICE_IDLE_CONSTANTS = "device_idle_constants";
 
         /**
-         * Device Idle (Doze) specific settings for watches. See {@code #DEVICE_IDLE_CONSTANTS}
-         *
-         * <p>
-         * Type: string
-         * @hide
-         * @see com.android.server.DeviceIdleController.Constants
-         */
-        public static final String DEVICE_IDLE_CONSTANTS_WATCH = "device_idle_constants_watch";
-
-        /**
          * Battery Saver specific settings
          * This is encoded as a key=value list, separated by commas. Ex:
          *
@@ -9404,9 +9394,12 @@
 
         /**
          * Battery anomaly detection specific settings
-         * This is encoded as a key=value list, separated by commas. Ex:
+         * This is encoded as a key=value list, separated by commas.
+         * wakeup_blacklisted_tags is a string, encoded as a set of tags, encoded via
+         * {@link Uri#encode(String)}, separated by colons. Ex:
          *
-         * "anomaly_detection_enabled=true,wakelock_threshold=2000"
+         * "anomaly_detection_enabled=true,wakelock_threshold=2000,wakeup_alarm_enabled=true,"
+         * "wakeup_alarm_threshold=10,wakeup_blacklisted_tags=tag1:tag2:with%2Ccomma:with%3Acolon"
          *
          * The following keys are supported:
          *
@@ -9414,6 +9407,11 @@
          * anomaly_detection_enabled       (boolean)
          * wakelock_enabled                (boolean)
          * wakelock_threshold              (long)
+         * wakeup_alarm_enabled            (boolean)
+         * wakeup_alarm_threshold          (long)
+         * wakeup_blacklisted_tags         (string)
+         * bluetooth_scan_enabled          (boolean)
+         * bluetooth_scan_threshold        (long)
          * </pre>
          * @hide
          */
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 1521e7e..2e59f6c 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -187,7 +187,7 @@
  * protect a dataset that contains sensitive information by requiring dataset authentication
  * (see {@link Dataset.Builder#setAuthentication(android.content.IntentSender)}), and to include
  * info about the "primary" field of the partition in the custom presentation for "secondary"
- * fields &mdash; that would prevent a malicious app from getting the "primary" fields without the
+ * fields&mdash;that would prevent a malicious app from getting the "primary" fields without the
  * user realizing they're being released (for example, a malicious app could have fields for a
  * credit card number, verification code, and expiration date crafted in a way that just the latter
  * is visible; by explicitly indicating the expiration date is related to a given credit card
diff --git a/core/java/android/service/autofill/ImageTransformation.java b/core/java/android/service/autofill/ImageTransformation.java
index 2151f74..4afda24 100644
--- a/core/java/android/service/autofill/ImageTransformation.java
+++ b/core/java/android/service/autofill/ImageTransformation.java
@@ -20,11 +20,12 @@
 
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 import android.util.Log;
-import android.util.Pair;
 import android.view.autofill.AutofillId;
 import android.widget.ImageView;
 import android.widget.RemoteViews;
@@ -43,9 +44,9 @@
  *
  * <pre class="prettyprint">
  *   new ImageTransformation.Builder(ccNumberId, Pattern.compile("^4815.*$"),
- *                                   R.drawable.ic_credit_card_logo1)
- *     .addOption(Pattern.compile("^1623.*$"), R.drawable.ic_credit_card_logo2)
- *     .addOption(Pattern.compile("^42.*$"), R.drawable.ic_credit_card_logo3)
+ *                                   R.drawable.ic_credit_card_logo1, "Brand 1")
+ *     .addOption(Pattern.compile("^1623.*$"), R.drawable.ic_credit_card_logo2, "Brand 2")
+ *     .addOption(Pattern.compile("^42.*$"), R.drawable.ic_credit_card_logo3, "Brand 3")
  *     .build();
  * </pre>
  *
@@ -59,7 +60,7 @@
     private static final String TAG = "ImageTransformation";
 
     private final AutofillId mId;
-    private final ArrayList<Pair<Pattern, Integer>> mOptions;
+    private final ArrayList<Option> mOptions;
 
     private ImageTransformation(Builder builder) {
         mId = builder.mId;
@@ -82,17 +83,21 @@
         }
 
         for (int i = 0; i < size; i++) {
-            final Pair<Pattern, Integer> option = mOptions.get(i);
+            final Option option = mOptions.get(i);
             try {
-                if (option.first.matcher(value).matches()) {
+                if (option.pattern.matcher(value).matches()) {
                     Log.d(TAG, "Found match at " + i + ": " + option);
-                    parentTemplate.setImageViewResource(childViewId, option.second);
+                    parentTemplate.setImageViewResource(childViewId, option.resId);
+                    if (option.contentDescription != null) {
+                        parentTemplate.setContentDescription(childViewId,
+                                option.contentDescription);
+                    }
                     return;
                 }
             } catch (Exception e) {
                 // Do not log full exception to avoid PII leaking
-                Log.w(TAG, "Error matching regex #" + i + "(" + option.first.pattern() + ") on id "
-                        + option.second + ": " + e.getClass());
+                Log.w(TAG, "Error matching regex #" + i + "(" + option.pattern + ") on id "
+                        + option.resId + ": " + e.getClass());
                 throw e;
 
             }
@@ -105,25 +110,44 @@
      */
     public static class Builder {
         private final AutofillId mId;
-        private final ArrayList<Pair<Pattern, Integer>> mOptions = new ArrayList<>();
+        private final ArrayList<Option> mOptions = new ArrayList<>();
         private boolean mDestroyed;
 
         /**
-         * Create a new builder for a autofill id and add a first option.
+         * Creates a new builder for a autofill id and add a first option.
          *
          * @param id id of the screen field that will be used to evaluate whether the image should
          * be used.
          * @param regex regular expression defining what should be matched to use this image.
          * @param resId resource id of the image (in the autofill service's package). The
          * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
+         *
+         * @deprecated use
+         * {@link #ImageTransformation.Builder(AutofillId, Pattern, int, CharSequence)} instead.
          */
+        @Deprecated
         public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId) {
             mId = Preconditions.checkNotNull(id);
-
             addOption(regex, resId);
         }
 
         /**
+         * Creates a new builder for a autofill id and add a first option.
+         *
+         * @param id id of the screen field that will be used to evaluate whether the image should
+         * be used.
+         * @param regex regular expression defining what should be matched to use this image.
+         * @param resId resource id of the image (in the autofill service's package). The
+         * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
+         * @param contentDescription content description to be applied in the child view.
+         */
+        public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId,
+                @NonNull CharSequence contentDescription) {
+            mId = Preconditions.checkNotNull(id);
+            addOption(regex, resId, contentDescription);
+        }
+
+        /**
          * Adds an option to replace the child view with a different image when the regex matches.
          *
          * @param regex regular expression defining what should be matched to use this image.
@@ -131,17 +155,43 @@
          * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
          *
          * @return this build
+         *
+         * @deprecated use {@link #addOption(Pattern, int, CharSequence)} instead.
          */
+        @Deprecated
         public Builder addOption(@NonNull Pattern regex, @DrawableRes int resId) {
+            addOptionInternal(regex, resId, null);
+            return this;
+        }
+
+        /**
+         * Adds an option to replace the child view with a different image and content description
+         * when the regex matches.
+         *
+         * @param regex regular expression defining what should be matched to use this image.
+         * @param resId resource id of the image (in the autofill service's package). The
+         * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
+         * @param contentDescription content description to be applied in the child view.
+         *
+         * @return this build
+         */
+        public Builder addOption(@NonNull Pattern regex, @DrawableRes int resId,
+                @NonNull CharSequence contentDescription) {
+            addOptionInternal(regex, resId, Preconditions.checkNotNull(contentDescription));
+            return this;
+        }
+
+        private void addOptionInternal(@NonNull Pattern regex, @DrawableRes int resId,
+                @Nullable CharSequence contentDescription) {
             throwIfDestroyed();
 
             Preconditions.checkNotNull(regex);
             Preconditions.checkArgument(resId != 0);
 
-            mOptions.add(new Pair<>(regex, resId));
-            return this;
+            mOptions.add(new Option(regex, resId, contentDescription));
         }
 
+
         /**
          * Creates a new {@link ImageTransformation} instance.
          */
@@ -178,15 +228,18 @@
         parcel.writeParcelable(mId, flags);
 
         final int size = mOptions.size();
-        final Pattern[] regexs = new Pattern[size];
+        final Pattern[] patterns = new Pattern[size];
         final int[] resIds = new int[size];
+        final CharSequence[] contentDescriptions = new String[size];
         for (int i = 0; i < size; i++) {
-            Pair<Pattern, Integer> regex = mOptions.get(i);
-            regexs[i] = regex.first;
-            resIds[i] = regex.second;
+            final Option option = mOptions.get(i);
+            patterns[i] = option.pattern;
+            resIds[i] = option.resId;
+            contentDescriptions[i] = option.contentDescription;
         }
-        parcel.writeSerializable(regexs);
+        parcel.writeSerializable(patterns);
         parcel.writeIntArray(resIds);
+        parcel.writeCharSequenceArray(contentDescriptions);
     }
 
     public static final Parcelable.Creator<ImageTransformation> CREATOR =
@@ -197,15 +250,22 @@
 
             final Pattern[] regexs = (Pattern[]) parcel.readSerializable();
             final int[] resIds = parcel.createIntArray();
+            final CharSequence[] contentDescriptions = parcel.readCharSequenceArray();
 
             // Always go through the builder to ensure the data ingested by the system obeys the
             // contract of the builder to avoid attacks using specially crafted parcels.
-            final ImageTransformation.Builder builder = new ImageTransformation.Builder(id,
-                    regexs[0], resIds[0]);
+            final CharSequence contentDescription = contentDescriptions[0];
+            final ImageTransformation.Builder builder = (contentDescription != null)
+                    ? new ImageTransformation.Builder(id, regexs[0], resIds[0], contentDescription)
+                    : new ImageTransformation.Builder(id, regexs[0], resIds[0]);
 
             final int size = regexs.length;
             for (int i = 1; i < size; i++) {
-                builder.addOption(regexs[i], resIds[i]);
+                if (contentDescriptions[i] != null) {
+                    builder.addOption(regexs[i], resIds[i], contentDescriptions[i]);
+                } else {
+                    builder.addOption(regexs[i], resIds[i]);
+                }
             }
 
             return builder.build();
@@ -216,4 +276,16 @@
             return new ImageTransformation[size];
         }
     };
+
+    private static final class Option {
+        public final Pattern pattern;
+        public final int resId;
+        public final CharSequence contentDescription;
+
+        Option(Pattern pattern, int resId, CharSequence contentDescription) {
+            this.pattern = pattern;
+            this.resId = resId;
+            this.contentDescription = TextUtils.trimNoCopySpans(contentDescription);
+        }
+    }
 }
diff --git a/core/java/android/service/autofill/InternalSanitizer.java b/core/java/android/service/autofill/InternalSanitizer.java
new file mode 100644
index 0000000..95d2f66
--- /dev/null
+++ b/core/java/android/service/autofill/InternalSanitizer.java
@@ -0,0 +1,38 @@
+/*
+ * 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.service.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcelable;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Superclass of all sanitizers the system understands. As this is not public all public subclasses
+ * have to implement {@link Sanitizer} again.
+ *
+ * @hide
+ */
+@TestApi
+public abstract class InternalSanitizer implements Sanitizer, Parcelable {
+
+    /**
+     * Sanitizes an {@link AutofillValue}.
+     *
+     * @hide
+     */
+    public abstract AutofillValue sanitize(@NonNull AutofillValue value);
+}
diff --git a/core/java/android/service/autofill/Sanitizer.java b/core/java/android/service/autofill/Sanitizer.java
new file mode 100644
index 0000000..38757ac
--- /dev/null
+++ b/core/java/android/service/autofill/Sanitizer.java
@@ -0,0 +1,26 @@
+/*
+ * 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.service.autofill;
+
+/**
+ * Helper class used to sanitize user input before using it in a save request.
+ *
+ * <p>Typically used to avoid displaying the save UI for values that are autofilled but reformatted
+ * by the app&mdash;for example, if the autofill service sends a credit card number
+ * value as "004815162342108" and the app automatically changes it to "0048 1516 2342 108".
+ */
+public interface Sanitizer {
+}
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index e0a0730..1b9240c 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -25,6 +25,8 @@
 import android.content.IntentSender;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
@@ -232,6 +234,8 @@
     private final int mFlags;
     private final CustomDescription mCustomDescription;
     private final InternalValidator mValidator;
+    private final InternalSanitizer[] mSanitizerKeys;
+    private final AutofillId[][] mSanitizerValues;
 
     private SaveInfo(Builder builder) {
         mType = builder.mType;
@@ -243,6 +247,18 @@
         mFlags = builder.mFlags;
         mCustomDescription = builder.mCustomDescription;
         mValidator = builder.mValidator;
+        if (builder.mSanitizers == null) {
+            mSanitizerKeys = null;
+            mSanitizerValues = null;
+        } else {
+            final int size = builder.mSanitizers.size();
+            mSanitizerKeys = new InternalSanitizer[size];
+            mSanitizerValues = new AutofillId[size][];
+            for (int i = 0; i < size; i++) {
+                mSanitizerKeys[i] = builder.mSanitizers.keyAt(i);
+                mSanitizerValues[i] = builder.mSanitizers.valueAt(i);
+            }
+        }
     }
 
     /** @hide */
@@ -292,6 +308,18 @@
         return mValidator;
     }
 
+    /** @hide */
+    @Nullable
+    public InternalSanitizer[] getSanitizerKeys() {
+        return mSanitizerKeys;
+    }
+
+    /** @hide */
+    @Nullable
+    public AutofillId[][] getSanitizerValues() {
+        return mSanitizerValues;
+    }
+
     /**
      * A builder for {@link SaveInfo} objects.
      */
@@ -307,6 +335,9 @@
         private int mFlags;
         private CustomDescription mCustomDescription;
         private InternalValidator mValidator;
+        private ArrayMap<InternalSanitizer, AutofillId[]> mSanitizers;
+        // Set used to validate against duplicate ids.
+        private ArraySet<AutofillId> mSanitizerIds;
 
         /**
          * Creates a new builder.
@@ -530,6 +561,61 @@
         }
 
         /**
+         * Adds a sanitizer for one or more field.
+         *
+         * <p>When a sanitizer is set for a field, the {@link AutofillValue} is sent to the
+         * sanitizer before a save request is <a href="#TriggeringSaveRequest">triggered</a>.
+         *
+         * <p>Typically used to avoid displaying the save UI for values that are autofilled but
+         * reformattedby the app. For example, to remove spaces between every 4 digits of a
+         * credit card number:
+         *
+         * <pre class="prettyprint">
+         * builder.addSanitizer(
+         *     new TextValueSanitizer(Pattern.compile("^(\\d{4}\s?\\d{4}\s?\\d{4}\s?\\d{4})$"),
+         *         "$1$2$3$4"), ccNumberId);
+         * </pre>
+         *
+         * <p>The same sanitizer can be reused to sanitize multiple fields. For example, to trim
+         * both the username and password fields:
+         *
+         * <pre class="prettyprint">
+         * builder.addSanitizer(
+         *     new TextValueSanitizer(Pattern.compile("^\\s*(.*)\\s*$"), "$1"),
+         *         usernameId, passwordId);
+         * </pre>
+         *
+         * @param sanitizer an implementation provided by the Android System.
+         * @param ids id of fields whose value will be sanitized.
+         * @return this builder.
+         *
+         * @throws IllegalArgumentException if a sanitizer for any of the {@code ids} has already
+         * been added or if {@code ids} is empty.
+         */
+        public @NonNull Builder addSanitizer(@NonNull Sanitizer sanitizer,
+                @NonNull AutofillId... ids) {
+            throwIfDestroyed();
+            Preconditions.checkArgument(!ArrayUtils.isEmpty(ids), "ids cannot be empty or null");
+            Preconditions.checkArgument((sanitizer instanceof InternalSanitizer),
+                    "not provided by Android System: " + sanitizer);
+
+            if (mSanitizers == null) {
+                mSanitizers = new ArrayMap<>();
+                mSanitizerIds = new ArraySet<>(ids.length);
+            }
+
+            // Check for duplicates first.
+            for (AutofillId id : ids) {
+                Preconditions.checkArgument(!mSanitizerIds.contains(id), "already added %s", id);
+                mSanitizerIds.add(id);
+            }
+
+            mSanitizers.put((InternalSanitizer) sanitizer, ids);
+
+            return this;
+        }
+
+        /**
          * Builds a new {@link SaveInfo} instance.
          *
          * @throws IllegalStateException if no
@@ -569,6 +655,10 @@
                 .append(", mFlags=").append(mFlags)
                 .append(", mCustomDescription=").append(mCustomDescription)
                 .append(", validation=").append(mValidator)
+                .append(", sanitizerKeys=")
+                    .append(mSanitizerKeys == null ? "N/A:" : mSanitizerKeys.length)
+                .append(", sanitizerValues=")
+                    .append(mSanitizerValues == null ? "N/A:" : mSanitizerValues.length)
                 .append("]").toString();
     }
 
@@ -591,6 +681,12 @@
         parcel.writeCharSequence(mDescription);
         parcel.writeParcelable(mCustomDescription, flags);
         parcel.writeParcelable(mValidator, flags);
+        parcel.writeParcelableArray(mSanitizerKeys, flags);
+        if (mSanitizerKeys != null) {
+            for (int i = 0; i < mSanitizerValues.length; i++) {
+                parcel.writeParcelableArray(mSanitizerValues[i], flags);
+            }
+        }
         parcel.writeInt(mFlags);
     }
 
@@ -621,6 +717,16 @@
             if (validator != null) {
                 builder.setValidator(validator);
             }
+            final InternalSanitizer[] sanitizers =
+                    parcel.readParcelableArray(null, InternalSanitizer.class);
+            if (sanitizers != null) {
+                final int size = sanitizers.length;
+                for (int i = 0; i < size; i++) {
+                    final AutofillId[] autofillIds =
+                            parcel.readParcelableArray(null, AutofillId.class);
+                    builder.addSanitizer(sanitizers[i], autofillIds);
+                }
+            }
             builder.setFlags(parcel.readInt());
             return builder.build();
         }
diff --git a/core/java/android/service/autofill/TextValueSanitizer.java b/core/java/android/service/autofill/TextValueSanitizer.java
new file mode 100644
index 0000000..12e85b1
--- /dev/null
+++ b/core/java/android/service/autofill/TextValueSanitizer.java
@@ -0,0 +1,122 @@
+/*
+ * 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.service.autofill;
+
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Slog;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Sanitizes a text {@link AutofillValue} using a regular expression (regex) substitution.
+ *
+ * <p>For example, to remove spaces from groups of 4-digits in a credit card:
+ *
+ * <pre class="prettyprint">
+ * new TextValueSanitizer(Pattern.compile("^(\\d{4}\s?\\d{4}\s?\\d{4}\s?\\d{4})$"), "$1$2$3$4")
+ * </pre>
+ */
+public final class TextValueSanitizer extends InternalSanitizer implements
+        Sanitizer, Parcelable {
+    private static final String TAG = "TextValueSanitizer";
+
+    private final Pattern mRegex;
+    private final String mSubst;
+
+    /**
+     * Default constructor.
+     *
+     * @param regex regular expression with groups (delimited by {@code (} and {@code (}) that
+     * are used to substitute parts of the {@link AutofillValue#getTextValue() text value}.
+     * @param subst the string that substitutes the matched regex, using {@code $} for
+     * group substitution ({@code $1} for 1st group match, {@code $2} for 2nd, etc).
+     */
+    public TextValueSanitizer(@NonNull Pattern regex, @NonNull String subst) {
+        mRegex = Preconditions.checkNotNull(regex);
+        mSubst = Preconditions.checkNotNull(subst);
+    }
+
+    /** @hide */
+    @Override
+    @TestApi
+    public AutofillValue sanitize(@NonNull AutofillValue value) {
+        if (value == null) {
+            Slog.w(TAG, "sanitize() called with null value");
+            return null;
+        }
+        if (!value.isText()) return value;
+
+        final CharSequence text = value.getTextValue();
+
+        try {
+            final Matcher matcher = mRegex.matcher(text);
+            if (!matcher.matches()) return value;
+
+            final CharSequence sanitized = matcher.replaceAll(mSubst);
+            return AutofillValue.forText(sanitized);
+        } catch (Exception e) {
+            Slog.w(TAG, "Exception evaluating " + mRegex + "/" + mSubst + ": " + e);
+            return value;
+        }
+    }
+
+    /////////////////////////////////////
+    // Object "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public String toString() {
+        if (!sDebug) return super.toString();
+
+        return "TextValueSanitizer: [regex=" + mRegex + ", subst=" + mSubst + "]";
+    }
+
+    /////////////////////////////////////
+    // Parcelable "contract" methods. //
+    /////////////////////////////////////
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeSerializable(mRegex);
+        parcel.writeString(mSubst);
+    }
+
+    public static final Parcelable.Creator<TextValueSanitizer> CREATOR =
+            new Parcelable.Creator<TextValueSanitizer>() {
+        @Override
+        public TextValueSanitizer createFromParcel(Parcel parcel) {
+            return new TextValueSanitizer((Pattern) parcel.readSerializable(), parcel.readString());
+        }
+
+        @Override
+        public TextValueSanitizer[] newArray(int size) {
+            return new TextValueSanitizer[size];
+        }
+    };
+}
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index ce678fc..7348cf6 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -56,6 +56,15 @@
     public static final String KEY_GROUP_KEY = "key_group_key";
 
     /**
+     * Data type: int, one of {@link NotificationListenerService.Ranking#USER_SENTIMENT_POSITIVE},
+     * {@link NotificationListenerService.Ranking#USER_SENTIMENT_NEUTRAL},
+     * {@link NotificationListenerService.Ranking#USER_SENTIMENT_NEGATIVE}. Used to express how
+     * a user feels about notifications in the same {@link android.app.NotificationChannel} as
+     * the notification represented by {@link #getKey()}.
+     */
+    public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
+
+    /**
      * Create a notification adjustment.
      *
      * @param pkg The package of the notification.
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index ed44f25..c388367 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -19,6 +19,7 @@
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.os.UserHandle;
+import android.service.notification.NotificationStats;
 import android.service.notification.IStatusBarNotificationHolder;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.NotificationRankingUpdate;
@@ -26,12 +27,13 @@
 /** @hide */
 oneway interface INotificationListener
 {
-    // listeners and rankers
+    // listeners and assistant
     void onListenerConnected(in NotificationRankingUpdate update);
     void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
             in NotificationRankingUpdate update);
+    // stats only for assistant
     void onNotificationRemoved(in IStatusBarNotificationHolder notificationHolder,
-            in NotificationRankingUpdate update, int reason);
+            in NotificationRankingUpdate update, in NotificationStats stats, int reason);
     void onNotificationRankingUpdate(in NotificationRankingUpdate update);
     void onListenerHintsChanged(int hints);
     void onInterruptionFilterChanged(int interruptionFilter);
@@ -40,7 +42,7 @@
     void onNotificationChannelModification(String pkgName, in UserHandle user, in NotificationChannel channel, int modificationType);
     void onNotificationChannelGroupModification(String pkgName, in UserHandle user, in NotificationChannelGroup group, int modificationType);
 
-    // rankers only
+    // assistants only
     void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder);
     void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId);
 }
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index d94017c..8e52bfa 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -16,12 +16,9 @@
 
 package android.service.notification;
 
-import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
-import android.app.NotificationChannel;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
@@ -30,9 +27,9 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
+
 import com.android.internal.os.SomeArgs;
 
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -79,7 +76,7 @@
             String snoozeCriterionId);
 
     /**
-     * A notification was posted by an app. Called before alert.
+     * A notification was posted by an app. Called before post.
      *
      * @param sbn the new notification
      * @return an adjustment or null to take no action, within 100ms.
@@ -87,6 +84,34 @@
     abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn);
 
     /**
+     * Implement this method to learn when notifications are removed, how they were interacted with
+     * before removal, and why they were removed.
+     * <p>
+     * This might occur because the user has dismissed the notification using system UI (or another
+     * notification listener) or because the app has withdrawn the notification.
+     * <p>
+     * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
+     * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
+     * fields such as {@link android.app.Notification#contentView} and
+     * {@link android.app.Notification#largeIcon}. However, all other fields on
+     * {@link StatusBarNotification}, sufficient to match this call with a prior call to
+     * {@link #onNotificationPosted(StatusBarNotification)}, will be intact.
+     *
+     ** @param sbn A data structure encapsulating at least the original information (tag and id)
+     *            and source (package name) used to post the {@link android.app.Notification} that
+     *            was just removed.
+     * @param rankingMap The current ranking map that can be used to retrieve ranking information
+     *                   for active notifications.
+     * @param stats Stats about how the user interacted with the notification before it was removed.
+     * @param reason see {@link #REASON_LISTENER_CANCEL}, etc.
+     */
+    @Override
+    public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
+            NotificationStats stats, int reason) {
+        onNotificationRemoved(sbn, rankingMap, reason);
+    }
+
+    /**
      * Updates a notification.  N.B. this won’t cause
      * an existing notification to alert, but might allow a future update to
      * this notification to alert.
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index a5223fd..08d3118 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.Notification.Builder;
@@ -265,7 +266,10 @@
     @GuardedBy("mLock")
     private RankingMap mRankingMap;
 
-    private INotificationManager mNoMan;
+    /**
+     * @hide
+     */
+    protected INotificationManager mNoMan;
 
     /**
      * Only valid after a successful call to (@link registerAsService}.
@@ -389,6 +393,18 @@
     }
 
     /**
+     * NotificationStats are not populated for notification listeners, so fall back to
+     * {@link #onNotificationRemoved(StatusBarNotification, RankingMap, int)}.
+     *
+     * @hide
+     */
+    @TestApi
+    public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
+            NotificationStats stats, int reason) {
+        onNotificationRemoved(sbn, rankingMap, reason);
+    }
+
+    /**
      * Implement this method to learn about when the listener is enabled and connected to
      * the notification manager.  You are safe to call {@link #getActiveNotifications()}
      * at this time.
@@ -1200,7 +1216,7 @@
 
         @Override
         public void onNotificationRemoved(IStatusBarNotificationHolder sbnHolder,
-                NotificationRankingUpdate update, int reason) {
+                NotificationRankingUpdate update, NotificationStats stats, int reason) {
             StatusBarNotification sbn;
             try {
                 sbn = sbnHolder.get();
@@ -1215,6 +1231,7 @@
                 args.arg1 = sbn;
                 args.arg2 = mRankingMap;
                 args.arg3 = reason;
+                args.arg4 = stats;
                 mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_REMOVED,
                         args).sendToTarget();
             }
@@ -1324,6 +1341,26 @@
          * @hide */
         public static final int VISIBILITY_NO_OVERRIDE = NotificationManager.VISIBILITY_NO_OVERRIDE;
 
+        /**
+         * The user is likely to have a negative reaction to this notification.
+         */
+        public static final int USER_SENTIMENT_NEGATIVE = -1;
+        /**
+         * It is not known how the user will react to this notification.
+         */
+        public static final int USER_SENTIMENT_NEUTRAL = 0;
+        /**
+         * The user is likely to have a positive reaction to this notification.
+         */
+        public static final int USER_SENTIMENT_POSITIVE = 1;
+
+        /** @hide */
+        @IntDef(prefix = { "USER_SENTIMENT_" }, value = {
+                USER_SENTIMENT_NEGATIVE, USER_SENTIMENT_NEUTRAL, USER_SENTIMENT_POSITIVE
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface UserSentiment {}
+
         private String mKey;
         private int mRank = -1;
         private boolean mIsAmbient;
@@ -1341,6 +1378,7 @@
         // Notification assistant snooze criteria.
         private ArrayList<SnoozeCriterion> mSnoozeCriteria;
         private boolean mShowBadge;
+        private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
 
         public Ranking() {}
 
@@ -1436,6 +1474,17 @@
         }
 
         /**
+         * Returns how the system thinks the user feels about notifications from the
+         * channel provided by {@link #getChannel()}. You can use this information to expose
+         * controls to help the user block this channel's notifications, if the sentiment is
+         * {@link #USER_SENTIMENT_NEGATIVE}, or emphasize this notification if the sentiment is
+         * {@link #USER_SENTIMENT_POSITIVE}.
+         */
+        public int getUserSentiment() {
+            return mUserSentiment;
+        }
+
+        /**
          * If the {@link NotificationAssistantService} has added people to this notification, then
          * this will be non-null.
          * @hide
@@ -1471,7 +1520,8 @@
                 int visibilityOverride, int suppressedVisualEffects, int importance,
                 CharSequence explanation, String overrideGroupKey,
                 NotificationChannel channel, ArrayList<String> overridePeople,
-                ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge) {
+                ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
+                int userSentiment) {
             mKey = key;
             mRank = rank;
             mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1485,6 +1535,7 @@
             mOverridePeople = overridePeople;
             mSnoozeCriteria = snoozeCriteria;
             mShowBadge = showBadge;
+            mUserSentiment = userSentiment;
         }
 
         /**
@@ -1532,6 +1583,7 @@
         private ArrayMap<String, ArrayList<String>> mOverridePeople;
         private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria;
         private ArrayMap<String, Boolean> mShowBadge;
+        private ArrayMap<String, Integer> mUserSentiment;
 
         private RankingMap(NotificationRankingUpdate rankingUpdate) {
             mRankingUpdate = rankingUpdate;
@@ -1560,7 +1612,7 @@
                     getVisibilityOverride(key), getSuppressedVisualEffects(key),
                     getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
                     getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
-                    getShowBadge(key));
+                    getShowBadge(key), getUserSentiment(key));
             return rank >= 0;
         }
 
@@ -1677,6 +1729,17 @@
             return showBadge == null ? false : showBadge.booleanValue();
         }
 
+        private int getUserSentiment(String key) {
+            synchronized (this) {
+                if (mUserSentiment == null) {
+                    buildUserSentimentLocked();
+                }
+            }
+            Integer userSentiment = mUserSentiment.get(key);
+            return userSentiment == null
+                    ? Ranking.USER_SENTIMENT_NEUTRAL : userSentiment.intValue();
+        }
+
         // Locked by 'this'
         private void buildRanksLocked() {
             String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1776,6 +1839,15 @@
             }
         }
 
+        // Locked by 'this'
+        private void buildUserSentimentLocked() {
+            Bundle userSentiment = mRankingUpdate.getUserSentiment();
+            mUserSentiment = new ArrayMap<>(userSentiment.size());
+            for (String key : userSentiment.keySet()) {
+                mUserSentiment.put(key, userSentiment.getInt(key));
+            }
+        }
+
         // ----------- Parcelable
 
         @Override
@@ -1835,8 +1907,9 @@
                     StatusBarNotification sbn = (StatusBarNotification) args.arg1;
                     RankingMap rankingMap = (RankingMap) args.arg2;
                     int reason = (int) args.arg3;
+                    NotificationStats stats = (NotificationStats) args.arg4;
                     args.recycle();
-                    onNotificationRemoved(sbn, rankingMap, reason);
+                    onNotificationRemoved(sbn, rankingMap, stats, reason);
                 } break;
 
                 case MSG_ON_LISTENER_CONNECTED: {
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 326b212..6d51db0 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -35,12 +35,13 @@
     private final Bundle mOverridePeople;
     private final Bundle mSnoozeCriteria;
     private final Bundle mShowBadge;
+    private final Bundle mUserSentiment;
 
     public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
             Bundle visibilityOverrides, Bundle suppressedVisualEffects,
             int[] importance, Bundle explanation, Bundle overrideGroupKeys,
             Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
-            Bundle showBadge) {
+            Bundle showBadge, Bundle userSentiment) {
         mKeys = keys;
         mInterceptedKeys = interceptedKeys;
         mVisibilityOverrides = visibilityOverrides;
@@ -52,6 +53,7 @@
         mOverridePeople = overridePeople;
         mSnoozeCriteria = snoozeCriteria;
         mShowBadge = showBadge;
+        mUserSentiment = userSentiment;
     }
 
     public NotificationRankingUpdate(Parcel in) {
@@ -67,6 +69,7 @@
         mOverridePeople = in.readBundle();
         mSnoozeCriteria = in.readBundle();
         mShowBadge = in.readBundle();
+        mUserSentiment = in.readBundle();
     }
 
     @Override
@@ -87,6 +90,7 @@
         out.writeBundle(mOverridePeople);
         out.writeBundle(mSnoozeCriteria);
         out.writeBundle(mShowBadge);
+        out.writeBundle(mUserSentiment);
     }
 
     public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -143,4 +147,8 @@
     public Bundle getShowBadge() {
         return mShowBadge;
     }
+
+    public Bundle getUserSentiment() {
+        return mUserSentiment;
+    }
 }
diff --git a/core/java/android/service/notification/NotificationStats.aidl b/core/java/android/service/notification/NotificationStats.aidl
new file mode 100644
index 0000000..40f5548
--- /dev/null
+++ b/core/java/android/service/notification/NotificationStats.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.service.notification;
+
+parcelable NotificationStats;
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java
new file mode 100644
index 0000000..76d5328
--- /dev/null
+++ b/core/java/android/service/notification/NotificationStats.java
@@ -0,0 +1,256 @@
+/**
+ * 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.service.notification;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.RemoteInput;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @hide
+ */
+@TestApi
+@SystemApi
+public final class NotificationStats implements Parcelable {
+
+    private boolean mSeen;
+    private boolean mExpanded;
+    private boolean mDirectReplied;
+    private boolean mSnoozed;
+    private boolean mViewedSettings;
+    private boolean mInteracted;
+
+    /** @hide */
+    @IntDef(prefix = { "DISMISSAL_SURFACE_" }, value = {
+            DISMISSAL_NOT_DISMISSED, DISMISSAL_OTHER, DISMISSAL_PEEK, DISMISSAL_AOD, DISMISSAL_SHADE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DismissalSurface {}
+
+
+    private @DismissalSurface int mDismissalSurface = DISMISSAL_NOT_DISMISSED;
+
+    /**
+     * Notification has not been dismissed yet.
+     */
+    public static final int DISMISSAL_NOT_DISMISSED = -1;
+    /**
+     * Notification has been dismissed from a {@link NotificationListenerService} or the app
+     * itself.
+     */
+    public static final int DISMISSAL_OTHER = 0;
+    /**
+     * Notification has been dismissed while peeking.
+     */
+    public static final int DISMISSAL_PEEK = 1;
+    /**
+     * Notification has been dismissed from always on display.
+     */
+    public static final int DISMISSAL_AOD = 2;
+    /**
+     * Notification has been dismissed from the notification shade.
+     */
+    public static final int DISMISSAL_SHADE = 3;
+
+    public NotificationStats() {
+    }
+
+    protected NotificationStats(Parcel in) {
+        mSeen = in.readByte() != 0;
+        mExpanded = in.readByte() != 0;
+        mDirectReplied = in.readByte() != 0;
+        mSnoozed = in.readByte() != 0;
+        mViewedSettings = in.readByte() != 0;
+        mInteracted = in.readByte() != 0;
+        mDismissalSurface = in.readInt();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByte((byte) (mSeen ? 1 : 0));
+        dest.writeByte((byte) (mExpanded ? 1 : 0));
+        dest.writeByte((byte) (mDirectReplied ? 1 : 0));
+        dest.writeByte((byte) (mSnoozed ? 1 : 0));
+        dest.writeByte((byte) (mViewedSettings ? 1 : 0));
+        dest.writeByte((byte) (mInteracted ? 1 : 0));
+        dest.writeInt(mDismissalSurface);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<NotificationStats> CREATOR = new Creator<NotificationStats>() {
+        @Override
+        public NotificationStats createFromParcel(Parcel in) {
+            return new NotificationStats(in);
+        }
+
+        @Override
+        public NotificationStats[] newArray(int size) {
+            return new NotificationStats[size];
+        }
+    };
+
+    /**
+     * Returns whether the user has seen this notification at least once.
+     */
+    public boolean hasSeen() {
+        return mSeen;
+    }
+
+    /**
+     * Records that the user as seen this notification at least once.
+     */
+    public void setSeen() {
+        mSeen = true;
+    }
+
+    /**
+     * Returns whether the user has expanded this notification at least once.
+     */
+    public boolean hasExpanded() {
+        return mExpanded;
+    }
+
+    /**
+     * Records that the user has expanded this notification at least once.
+     */
+    public void setExpanded() {
+        mExpanded = true;
+        mInteracted = true;
+    }
+
+    /**
+     * Returns whether the user has replied to a notification that has a
+     * {@link android.app.Notification.Action.Builder#addRemoteInput(RemoteInput) direct reply} at
+     * least once.
+     */
+    public boolean hasDirectReplied() {
+        return mDirectReplied;
+    }
+
+    /**
+     * Records that the user has replied to a notification that has a
+     * {@link android.app.Notification.Action.Builder#addRemoteInput(RemoteInput) direct reply}
+     * at least once.
+     */
+    public void setDirectReplied() {
+        mDirectReplied = true;
+        mInteracted = true;
+    }
+
+    /**
+     * Returns whether the user has snoozed this notification at least once.
+     */
+    public boolean hasSnoozed() {
+        return mSnoozed;
+    }
+
+    /**
+     * Records that the user has snoozed this notification at least once.
+     */
+    public void setSnoozed() {
+        mSnoozed = true;
+        mInteracted = true;
+    }
+
+    /**
+     * Returns whether the user has viewed the in-shade settings for this notification at least
+     * once.
+     */
+    public boolean hasViewedSettings() {
+        return mViewedSettings;
+    }
+
+    /**
+     * Records that the user has viewed the in-shade settings for this notification at least once.
+     */
+    public void setViewedSettings() {
+        mViewedSettings = true;
+        mInteracted = true;
+    }
+
+    /**
+     * Returns whether the user has interacted with this notification beyond having viewed it.
+     */
+    public boolean hasInteracted() {
+        return mInteracted;
+    }
+
+    /**
+     * Returns from which surface the notification was dismissed.
+     */
+    public @DismissalSurface int getDismissalSurface() {
+        return mDismissalSurface;
+    }
+
+    /**
+     * Returns from which surface the notification was dismissed.
+     */
+    public void setDismissalSurface(@DismissalSurface int dismissalSurface) {
+        mDismissalSurface = dismissalSurface;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        NotificationStats that = (NotificationStats) o;
+
+        if (mSeen != that.mSeen) return false;
+        if (mExpanded != that.mExpanded) return false;
+        if (mDirectReplied != that.mDirectReplied) return false;
+        if (mSnoozed != that.mSnoozed) return false;
+        if (mViewedSettings != that.mViewedSettings) return false;
+        if (mInteracted != that.mInteracted) return false;
+        return mDismissalSurface == that.mDismissalSurface;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = (mSeen ? 1 : 0);
+        result = 31 * result + (mExpanded ? 1 : 0);
+        result = 31 * result + (mDirectReplied ? 1 : 0);
+        result = 31 * result + (mSnoozed ? 1 : 0);
+        result = 31 * result + (mViewedSettings ? 1 : 0);
+        result = 31 * result + (mInteracted ? 1 : 0);
+        result = 31 * result + mDismissalSurface;
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("NotificationStats{");
+        sb.append("mSeen=").append(mSeen);
+        sb.append(", mExpanded=").append(mExpanded);
+        sb.append(", mDirectReplied=").append(mDirectReplied);
+        sb.append(", mSnoozed=").append(mSnoozed);
+        sb.append(", mViewedSettings=").append(mViewedSettings);
+        sb.append(", mInteracted=").append(mInteracted);
+        sb.append(", mDismissalSurface=").append(mDismissalSurface);
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/core/java/android/slice/Slice.java b/core/java/android/slice/Slice.java
index bb810e6..5768654 100644
--- a/core/java/android/slice/Slice.java
+++ b/core/java/android/slice/Slice.java
@@ -35,6 +35,8 @@
 import android.os.Parcelable;
 import android.widget.RemoteViews;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 
@@ -136,7 +138,7 @@
     }
 
     /**
-     * @return The Uri that this slice represents.
+     * @return The Uri that this Slice represents.
      */
     public Uri getUri() {
         return mUri;
@@ -191,6 +193,13 @@
     }
 
     /**
+     * @hide
+     */
+    public boolean hasHint(@SliceHint String hint) {
+        return ArrayUtils.contains(mHints, hint);
+    }
+
+    /**
      * A Builder used to construct {@link Slice}s
      */
     public static class Builder {
@@ -308,4 +317,31 @@
             return new Slice[size];
         }
     };
+
+    /**
+     * @hide
+     * @return A string representation of this slice.
+     */
+    public String getString() {
+        return getString("");
+    }
+
+    private String getString(String indent) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < mItems.length; i++) {
+            sb.append(indent);
+            if (mItems[i].getType() == TYPE_SLICE) {
+                sb.append("slice:\n");
+                sb.append(mItems[i].getSlice().getString(indent + "   "));
+            } else if (mItems[i].getType() == TYPE_TEXT) {
+                sb.append("text: ");
+                sb.append(mItems[i].getText());
+                sb.append("\n");
+            } else {
+                sb.append(SliceItem.typeToString(mItems[i].getType()));
+                sb.append("\n");
+            }
+        }
+        return sb.toString();
+    }
 }
diff --git a/core/java/android/slice/SliceItem.java b/core/java/android/slice/SliceItem.java
index 16f7dc6..2827ab9 100644
--- a/core/java/android/slice/SliceItem.java
+++ b/core/java/android/slice/SliceItem.java
@@ -132,6 +132,13 @@
         mHints = ArrayUtils.appendElement(String.class, mHints, hint);
     }
 
+    /**
+     * @hide
+     */
+    public void removeHint(String hint) {
+        ArrayUtils.removeElement(String.class, mHints, hint);
+    }
+
     public @SliceType int getType() {
         return mType;
     }
@@ -230,7 +237,7 @@
     public boolean hasHints(@SliceHint String[] hints) {
         if (hints == null) return true;
         for (String hint : hints) {
-            if (!ArrayUtils.contains(mHints, hint)) {
+            if (!TextUtils.isEmpty(hint) && !ArrayUtils.contains(mHints, hint)) {
                 return false;
             }
         }
@@ -241,7 +248,7 @@
      * @hide
      */
     public boolean hasAnyHints(@SliceHint String[] hints) {
-        if (hints == null) return true;
+        if (hints == null) return false;
         for (String hint : hints) {
             if (ArrayUtils.contains(mHints, hint)) {
                 return true;
@@ -309,4 +316,29 @@
             return new SliceItem[size];
         }
     };
+
+    /**
+     * @hide
+     */
+    public static String typeToString(int type) {
+        switch (type) {
+            case TYPE_SLICE:
+                return "Slice";
+            case TYPE_TEXT:
+                return "Text";
+            case TYPE_IMAGE:
+                return "Image";
+            case TYPE_ACTION:
+                return "Action";
+            case TYPE_REMOTE_VIEW:
+                return "RemoteView";
+            case TYPE_COLOR:
+                return "Color";
+            case TYPE_TIMESTAMP:
+                return "Timestamp";
+            case TYPE_REMOTE_INPUT:
+                return "RemoteInput";
+        }
+        return "Unrecognized type: " + type;
+    }
 }
diff --git a/core/java/android/slice/SliceQuery.java b/core/java/android/slice/SliceQuery.java
index edac0cc..d99b26a 100644
--- a/core/java/android/slice/SliceQuery.java
+++ b/core/java/android/slice/SliceQuery.java
@@ -61,6 +61,13 @@
     /**
      * @hide
      */
+    public static List<SliceItem> findAll(SliceItem s, int type) {
+        return findAll(s, type, (String[]) null, null);
+    }
+
+    /**
+     * @hide
+     */
     public static List<SliceItem> findAll(SliceItem s, int type, String hints, String nonHints) {
         return findAll(s, type, new String[]{ hints }, new String[]{ nonHints });
     }
@@ -85,6 +92,13 @@
     /**
      * @hide
      */
+    public static SliceItem find(Slice s, int type) {
+        return find(s, type, (String[]) null, null);
+    }
+
+    /**
+     * @hide
+     */
     public static SliceItem find(SliceItem s, int type) {
         return find(s, type, (String[]) null, null);
     }
diff --git a/core/java/android/slice/views/ActionRow.java b/core/java/android/slice/views/ActionRow.java
new file mode 100644
index 0000000..93e9c03
--- /dev/null
+++ b/core/java/android/slice/views/ActionRow.java
@@ -0,0 +1,201 @@
+/*
+ * 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.slice.views;
+
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.os.AsyncTask;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewParent;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * @hide
+ */
+public class ActionRow extends FrameLayout {
+
+    private static final int MAX_ACTIONS = 5;
+    private final int mSize;
+    private final int mIconPadding;
+    private final LinearLayout mActionsGroup;
+    private final boolean mFullActions;
+    private int mColor = Color.BLACK;
+
+    public ActionRow(Context context, boolean fullActions) {
+        super(context);
+        mFullActions = fullActions;
+        mSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
+                context.getResources().getDisplayMetrics());
+        mIconPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12,
+                context.getResources().getDisplayMetrics());
+        mActionsGroup = new LinearLayout(context);
+        mActionsGroup.setOrientation(LinearLayout.HORIZONTAL);
+        mActionsGroup.setLayoutParams(
+                new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+        addView(mActionsGroup);
+    }
+
+    private void setColor(int color) {
+        mColor = color;
+        for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
+            View view = mActionsGroup.getChildAt(i);
+            SliceItem item = (SliceItem) view.getTag();
+            boolean tint = !item.hasHint(Slice.HINT_NO_TINT);
+            if (tint) {
+                ((ImageView) view).setImageTintList(ColorStateList.valueOf(mColor));
+            }
+        }
+    }
+
+    private ImageView addAction(Icon icon, boolean allowTint, SliceItem image) {
+        ImageView imageView = new ImageView(getContext());
+        imageView.setPadding(mIconPadding, mIconPadding, mIconPadding, mIconPadding);
+        imageView.setScaleType(ScaleType.FIT_CENTER);
+        imageView.setImageIcon(icon);
+        if (allowTint) {
+            imageView.setImageTintList(ColorStateList.valueOf(mColor));
+        }
+        imageView.setBackground(SliceViewUtil.getDrawable(getContext(),
+                android.R.attr.selectableItemBackground));
+        imageView.setTag(image);
+        addAction(imageView);
+        return imageView;
+    }
+
+    /**
+     * Set the actions and color for this action row.
+     */
+    public void setActions(SliceItem actionRow, SliceItem defColor) {
+        removeAllViews();
+        mActionsGroup.removeAllViews();
+        addView(mActionsGroup);
+
+        SliceItem color = SliceQuery.find(actionRow, SliceItem.TYPE_COLOR);
+        if (color == null) {
+            color = defColor;
+        }
+        if (color != null) {
+            setColor(color.getColor());
+        }
+        SliceQuery.findAll(actionRow, SliceItem.TYPE_ACTION).forEach(action -> {
+            if (mActionsGroup.getChildCount() >= MAX_ACTIONS) {
+                return;
+            }
+            SliceItem image = SliceQuery.find(action, SliceItem.TYPE_IMAGE);
+            if (image == null) {
+                return;
+            }
+            boolean tint = !image.hasHint(Slice.HINT_NO_TINT);
+            SliceItem input = SliceQuery.find(action, SliceItem.TYPE_REMOTE_INPUT);
+            if (input != null && input.getRemoteInput().getAllowFreeFormInput()) {
+                addAction(image.getIcon(), tint, image).setOnClickListener(
+                        v -> handleRemoteInputClick(v, action.getAction(), input.getRemoteInput()));
+                createRemoteInputView(mColor, getContext());
+            } else {
+                addAction(image.getIcon(), tint, image).setOnClickListener(v -> AsyncTask.execute(
+                        () -> {
+                            try {
+                                action.getAction().send();
+                            } catch (CanceledException e) {
+                                e.printStackTrace();
+                            }
+                        }));
+            }
+        });
+        setVisibility(getChildCount() != 0 ? View.VISIBLE : View.GONE);
+    }
+
+    private void addAction(View child) {
+        mActionsGroup.addView(child, new LinearLayout.LayoutParams(mSize, mSize, 1));
+    }
+
+    private void createRemoteInputView(int color, Context context) {
+        View riv = RemoteInputView.inflate(context, this);
+        riv.setVisibility(View.INVISIBLE);
+        addView(riv, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+        riv.setBackgroundColor(color);
+    }
+
+    private boolean handleRemoteInputClick(View view, PendingIntent pendingIntent,
+            RemoteInput input) {
+        if (input == null) {
+            return false;
+        }
+
+        ViewParent p = view.getParent().getParent();
+        RemoteInputView riv = null;
+        while (p != null) {
+            if (p instanceof View) {
+                View pv = (View) p;
+                riv = findRemoteInputView(pv);
+                if (riv != null) {
+                    break;
+                }
+            }
+            p = p.getParent();
+        }
+        if (riv == null) {
+            return false;
+        }
+
+        int width = view.getWidth();
+        if (view instanceof TextView) {
+            // Center the reveal on the text which might be off-center from the TextView
+            TextView tv = (TextView) view;
+            if (tv.getLayout() != null) {
+                int innerWidth = (int) tv.getLayout().getLineWidth(0);
+                innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
+                width = Math.min(width, innerWidth);
+            }
+        }
+        int cx = view.getLeft() + width / 2;
+        int cy = view.getTop() + view.getHeight() / 2;
+        int w = riv.getWidth();
+        int h = riv.getHeight();
+        int r = Math.max(
+                Math.max(cx + cy, cx + (h - cy)),
+                Math.max((w - cx) + cy, (w - cx) + (h - cy)));
+
+        riv.setRevealParameters(cx, cy, r);
+        riv.setPendingIntent(pendingIntent);
+        riv.setRemoteInput(new RemoteInput[] {
+                input
+        }, input);
+        riv.focusAnimated();
+        return true;
+    }
+
+    private RemoteInputView findRemoteInputView(View v) {
+        if (v == null) {
+            return null;
+        }
+        return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
+    }
+}
diff --git a/core/java/android/slice/views/GridView.java b/core/java/android/slice/views/GridView.java
new file mode 100644
index 0000000..18a90f7
--- /dev/null
+++ b/core/java/android/slice/views/GridView.java
@@ -0,0 +1,186 @@
+/*
+ * 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.slice.views;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.views.LargeSliceAdapter.SliceListView;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * @hide
+ */
+public class GridView extends LinearLayout implements SliceListView {
+
+    private static final String TAG = "GridView";
+
+    private static final int MAX_IMAGES = 3;
+    private static final int MAX_ALL = 5;
+    private boolean mIsAllImages;
+
+    public GridView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mIsAllImages) {
+            int width = MeasureSpec.getSize(widthMeasureSpec);
+            int height = width / getChildCount();
+            heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY,
+                    height);
+            getLayoutParams().height = height;
+            for (int i = 0; i < getChildCount(); i++) {
+                getChildAt(i).getLayoutParams().height = height;
+            }
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    public void setSliceItem(SliceItem slice) {
+        mIsAllImages = true;
+        removeAllViews();
+        int total = 1;
+        if (slice.getType() == SliceItem.TYPE_SLICE) {
+            SliceItem[] items = slice.getSlice().getItems();
+            total = items.length;
+            for (int i = 0; i < total; i++) {
+                SliceItem item = items[i];
+                if (isFull()) {
+                    continue;
+                }
+                if (!addItem(item)) {
+                    mIsAllImages = false;
+                }
+            }
+        } else {
+            if (!isFull()) {
+                if (!addItem(slice)) {
+                    mIsAllImages = false;
+                }
+            }
+        }
+        if (total > getChildCount() && mIsAllImages) {
+            addExtraCount(total - getChildCount());
+        }
+    }
+
+    private void addExtraCount(int numExtra) {
+        View last = getChildAt(getChildCount() - 1);
+        FrameLayout frame = new FrameLayout(getContext());
+        frame.setLayoutParams(last.getLayoutParams());
+
+        removeView(last);
+        frame.addView(last, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+        TextView v = new TextView(getContext());
+        v.setTextColor(Color.WHITE);
+        v.setBackgroundColor(0x4d000000);
+        v.setText(getResources().getString(R.string.slice_more_content, numExtra));
+        v.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
+        v.setGravity(Gravity.CENTER);
+        frame.addView(v, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+        addView(frame);
+    }
+
+    private boolean isFull() {
+        return getChildCount() >= (mIsAllImages ? MAX_IMAGES : MAX_ALL);
+    }
+
+    /**
+     * Returns true if this item is just an image.
+     */
+    private boolean addItem(SliceItem item) {
+        if (item.getType() == SliceItem.TYPE_IMAGE) {
+            ImageView v = new ImageView(getContext());
+            v.setImageIcon(item.getIcon());
+            v.setScaleType(ScaleType.CENTER_CROP);
+            addView(v, new LayoutParams(0, MATCH_PARENT, 1));
+            return true;
+        } else {
+            LinearLayout v = new LinearLayout(getContext());
+            int s = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                    12, getContext().getResources().getDisplayMetrics());
+            v.setPadding(0, s, 0, 0);
+            v.setOrientation(LinearLayout.VERTICAL);
+            v.setGravity(Gravity.CENTER_HORIZONTAL);
+            // TODO: Unify sporadic inflates that happen throughout the code.
+            ArrayList<SliceItem> items = new ArrayList<>();
+            if (item.getType() == SliceItem.TYPE_SLICE) {
+                items.addAll(Arrays.asList(item.getSlice().getItems()));
+            }
+            items.forEach(i -> {
+                Context context = getContext();
+                switch (i.getType()) {
+                    case SliceItem.TYPE_TEXT:
+                        boolean title = false;
+                        if ((item.hasAnyHints(new String[] {
+                                Slice.HINT_LARGE, Slice.HINT_TITLE
+                        }))) {
+                            title = true;
+                        }
+                        TextView tv = (TextView) LayoutInflater.from(context).inflate(
+                                title ? R.layout.slice_title : R.layout.slice_secondary_text, null);
+                        tv.setText(i.getText());
+                        v.addView(tv);
+                        break;
+                    case SliceItem.TYPE_IMAGE:
+                        ImageView iv = new ImageView(context);
+                        iv.setImageIcon(i.getIcon());
+                        if (item.hasHint(Slice.HINT_LARGE)) {
+                            iv.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+                        } else {
+                            int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                                    48, context.getResources().getDisplayMetrics());
+                            iv.setLayoutParams(new LayoutParams(size, size));
+                        }
+                        v.addView(iv);
+                        break;
+                    case SliceItem.TYPE_REMOTE_VIEW:
+                        v.addView(i.getRemoteView().apply(context, v));
+                        break;
+                    case SliceItem.TYPE_COLOR:
+                        // TODO: Support color to tint stuff here.
+                        break;
+                }
+            });
+            addView(v, new LayoutParams(0, WRAP_CONTENT, 1));
+            return false;
+        }
+    }
+}
diff --git a/core/java/android/slice/views/LargeSliceAdapter.java b/core/java/android/slice/views/LargeSliceAdapter.java
new file mode 100644
index 0000000..e77a1b2
--- /dev/null
+++ b/core/java/android/slice/views/LargeSliceAdapter.java
@@ -0,0 +1,224 @@
+/*
+ * 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.slice.views;
+
+import android.content.Context;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.slice.views.LargeSliceAdapter.SliceViewHolder;
+import android.util.ArrayMap;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
+
+import com.android.internal.R;
+import com.android.internal.widget.RecyclerView;
+import com.android.internal.widget.RecyclerView.ViewHolder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @hide
+ */
+public class LargeSliceAdapter extends RecyclerView.Adapter<SliceViewHolder> {
+
+    public static final int TYPE_DEFAULT       = 1;
+    public static final int TYPE_HEADER        = 2;
+    public static final int TYPE_GRID          = 3;
+    public static final int TYPE_MESSAGE       = 4;
+    public static final int TYPE_MESSAGE_LOCAL = 5;
+    public static final int TYPE_REMOTE_VIEWS  = 6;
+
+    private final IdGenerator mIdGen = new IdGenerator();
+    private final Context mContext;
+    private List<SliceWrapper> mSlices = new ArrayList<>();
+    private SliceItem mColor;
+
+    public LargeSliceAdapter(Context context) {
+        mContext = context;
+        setHasStableIds(true);
+    }
+
+    /**
+     * Set the {@link SliceItem}'s to be displayed in the adapter and the accent color.
+     */
+    public void setSliceItems(List<SliceItem> slices, SliceItem color) {
+        mColor = color;
+        mIdGen.resetUsage();
+        mSlices = slices.stream().map(s -> new SliceWrapper(s, mIdGen))
+                .collect(Collectors.toList());
+        notifyDataSetChanged();
+    }
+
+    @Override
+    public SliceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View v = inflateforType(viewType);
+        v.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+        return new SliceViewHolder(v);
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        return mSlices.get(position).mType;
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return mSlices.get(position).mId;
+    }
+
+    @Override
+    public int getItemCount() {
+        return mSlices.size();
+    }
+
+    @Override
+    public void onBindViewHolder(SliceViewHolder holder, int position) {
+        SliceWrapper slice = mSlices.get(position);
+        if (holder.mSliceView != null) {
+            holder.mSliceView.setColor(mColor);
+            holder.mSliceView.setSliceItem(slice.mItem);
+        } else if (slice.mType == TYPE_REMOTE_VIEWS) {
+            FrameLayout frame = (FrameLayout) holder.itemView;
+            frame.removeAllViews();
+            frame.addView(slice.mItem.getRemoteView().apply(mContext, frame));
+        }
+    }
+
+    private View inflateforType(int viewType) {
+        switch (viewType) {
+            case TYPE_REMOTE_VIEWS:
+                return new FrameLayout(mContext);
+            case TYPE_GRID:
+                return LayoutInflater.from(mContext).inflate(R.layout.slice_grid, null);
+            case TYPE_MESSAGE:
+                return LayoutInflater.from(mContext).inflate(R.layout.slice_message, null);
+            case TYPE_MESSAGE_LOCAL:
+                return LayoutInflater.from(mContext).inflate(R.layout.slice_message_local, null);
+        }
+        return new SmallTemplateView(mContext);
+    }
+
+    protected static class SliceWrapper {
+        private final SliceItem mItem;
+        private final int mType;
+        private final long mId;
+
+        public SliceWrapper(SliceItem item, IdGenerator idGen) {
+            mItem = item;
+            mType = getType(item);
+            mId = idGen.getId(item);
+        }
+
+        public static int getType(SliceItem item) {
+            if (item.getType() == SliceItem.TYPE_REMOTE_VIEW) {
+                return TYPE_REMOTE_VIEWS;
+            }
+            if (item.hasHint(Slice.HINT_MESSAGE)) {
+                // TODO: Better way to determine me or not? Something more like Messaging style.
+                if (SliceQuery.find(item, -1, Slice.HINT_SOURCE, null) != null) {
+                    return TYPE_MESSAGE;
+                } else {
+                    return TYPE_MESSAGE_LOCAL;
+                }
+            }
+            if (item.hasHint(Slice.HINT_HORIZONTAL)) {
+                return TYPE_GRID;
+            }
+            return TYPE_DEFAULT;
+        }
+    }
+
+    /**
+     * A {@link ViewHolder} for presenting slices in {@link LargeSliceAdapter}.
+     */
+    public static class SliceViewHolder extends ViewHolder {
+        public final SliceListView mSliceView;
+
+        public SliceViewHolder(View itemView) {
+            super(itemView);
+            mSliceView = itemView instanceof SliceListView ? (SliceListView) itemView : null;
+        }
+    }
+
+    /**
+     * View slices being displayed in {@link LargeSliceAdapter}.
+     */
+    public interface SliceListView {
+        /**
+         * Set the slice item for this view.
+         */
+        void setSliceItem(SliceItem slice);
+
+        /**
+         * Set the color for the items in this view.
+         */
+        default void setColor(SliceItem color) {
+
+        }
+    }
+
+    private static class IdGenerator {
+        private long mNextLong = 0;
+        private final ArrayMap<String, Long> mCurrentIds = new ArrayMap<>();
+        private final ArrayMap<String, Integer> mUsedIds = new ArrayMap<>();
+
+        public long getId(SliceItem item) {
+            String str = genString(item);
+            if (!mCurrentIds.containsKey(str)) {
+                mCurrentIds.put(str, mNextLong++);
+            }
+            long id = mCurrentIds.get(str);
+            int index = mUsedIds.getOrDefault(str, 0);
+            mUsedIds.put(str, index + 1);
+            return id + index * 10000;
+        }
+
+        private String genString(SliceItem item) {
+            StringBuilder builder = new StringBuilder();
+            SliceQuery.stream(item).forEach(i -> {
+                builder.append(i.getType());
+                i.removeHint(Slice.HINT_SELECTED);
+                builder.append(i.getHints());
+                switch (i.getType()) {
+                    case SliceItem.TYPE_REMOTE_VIEW:
+                        builder.append(i.getRemoteView());
+                        break;
+                    case SliceItem.TYPE_IMAGE:
+                        builder.append(i.getIcon());
+                        break;
+                    case SliceItem.TYPE_TEXT:
+                        builder.append(i.getText());
+                        break;
+                    case SliceItem.TYPE_COLOR:
+                        builder.append(i.getColor());
+                        break;
+                }
+            });
+            return builder.toString();
+        }
+
+        public void resetUsage() {
+            mUsedIds.clear();
+        }
+    }
+}
diff --git a/core/java/android/slice/views/LargeTemplateView.java b/core/java/android/slice/views/LargeTemplateView.java
new file mode 100644
index 0000000..d53e8fc
--- /dev/null
+++ b/core/java/android/slice/views/LargeTemplateView.java
@@ -0,0 +1,116 @@
+/*
+ * 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.slice.views;
+
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.content.Context;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.slice.views.SliceView.SliceModeView;
+import android.util.TypedValue;
+
+import com.android.internal.widget.LinearLayoutManager;
+import com.android.internal.widget.RecyclerView;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public class LargeTemplateView extends SliceModeView {
+    private final LargeSliceAdapter mAdapter;
+    private final RecyclerView mRecyclerView;
+    private final int mDefaultHeight;
+    private final int mMaxHeight;
+    private Slice mSlice;
+
+    public LargeTemplateView(Context context) {
+        super(context);
+
+        mRecyclerView = new RecyclerView(getContext());
+        mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+        mAdapter = new LargeSliceAdapter(context);
+        mRecyclerView.setAdapter(mAdapter);
+        addView(mRecyclerView);
+        int width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 300,
+                getResources().getDisplayMetrics());
+        setLayoutParams(new LayoutParams(width, WRAP_CONTENT));
+        mDefaultHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200,
+                getResources().getDisplayMetrics());
+        mMaxHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200,
+                getResources().getDisplayMetrics());
+    }
+
+    @Override
+    public String getMode() {
+        return SliceView.MODE_LARGE;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        mRecyclerView.getLayoutParams().height = WRAP_CONTENT;
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        if (mRecyclerView.getMeasuredHeight() > mMaxHeight
+                || mSlice.hasHint(Slice.HINT_PARTIAL)) {
+            mRecyclerView.getLayoutParams().height = mDefaultHeight;
+        } else {
+            mRecyclerView.getLayoutParams().height = mRecyclerView.getMeasuredHeight();
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
+    public void setSlice(Slice slice) {
+        SliceItem color = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
+        mSlice = slice;
+        List<SliceItem> items = new ArrayList<>();
+        boolean[] hasHeader = new boolean[1];
+        if (slice.hasHint(Slice.HINT_LIST)) {
+            addList(slice, items);
+        } else {
+            Arrays.asList(slice.getItems()).forEach(item -> {
+                if (item.hasHint(Slice.HINT_ACTIONS)) {
+                    return;
+                } else if (item.getType() == SliceItem.TYPE_COLOR) {
+                    return;
+                } else if (item.getType() == SliceItem.TYPE_SLICE
+                        && item.hasHint(Slice.HINT_LIST)) {
+                    addList(item.getSlice(), items);
+                } else if (item.hasHint(Slice.HINT_LIST_ITEM)) {
+                    items.add(item);
+                } else if (!hasHeader[0]) {
+                    hasHeader[0] = true;
+                    items.add(0, item);
+                } else {
+                    item.addHint(Slice.HINT_LIST_ITEM);
+                    items.add(item);
+                }
+            });
+        }
+        mAdapter.setSliceItems(items, color);
+    }
+
+    private void addList(Slice slice, List<SliceItem> items) {
+        List<SliceItem> sliceItems = Arrays.asList(slice.getItems());
+        sliceItems.forEach(i -> i.addHint(Slice.HINT_LIST_ITEM));
+        items.addAll(sliceItems);
+    }
+}
diff --git a/core/java/android/slice/views/MessageView.java b/core/java/android/slice/views/MessageView.java
new file mode 100644
index 0000000..7b03e0b
--- /dev/null
+++ b/core/java/android/slice/views/MessageView.java
@@ -0,0 +1,77 @@
+/*
+ * 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.slice.views;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.slice.views.LargeSliceAdapter.SliceListView;
+import android.text.SpannableStringBuilder;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * @hide
+ */
+public class MessageView extends LinearLayout implements SliceListView {
+
+    private TextView mDetails;
+    private ImageView mIcon;
+
+    public MessageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mDetails = findViewById(android.R.id.summary);
+        mIcon = findViewById(android.R.id.icon);
+    }
+
+    @Override
+    public void setSliceItem(SliceItem slice) {
+        SliceItem source = SliceQuery.find(slice, SliceItem.TYPE_IMAGE, Slice.HINT_SOURCE, null);
+        if (source != null) {
+            final int iconSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                    24, getContext().getResources().getDisplayMetrics());
+            // TODO try and turn this into a drawable
+            Bitmap iconBm = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
+            Canvas iconCanvas = new Canvas(iconBm);
+            Drawable d = source.getIcon().loadDrawable(getContext());
+            d.setBounds(0, 0, iconSize, iconSize);
+            d.draw(iconCanvas);
+            mIcon.setImageBitmap(SliceViewUtil.getCircularBitmap(iconBm));
+        }
+        SpannableStringBuilder builder = new SpannableStringBuilder();
+        SliceQuery.findAll(slice, SliceItem.TYPE_TEXT).forEach(text -> {
+            if (builder.length() != 0) {
+                builder.append('\n');
+            }
+            builder.append(text.getText());
+        });
+        mDetails.setText(builder.toString());
+    }
+
+}
diff --git a/core/java/android/slice/views/RemoteInputView.java b/core/java/android/slice/views/RemoteInputView.java
new file mode 100644
index 0000000..a29bb5c
--- /dev/null
+++ b/core/java/android/slice/views/RemoteInputView.java
@@ -0,0 +1,445 @@
+/*
+ * 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.slice.views;
+
+import android.animation.Animator;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutManager;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.internal.R;
+
+/**
+ * Host for the remote input.
+ *
+ * @hide
+ */
+// TODO this should be unified with SystemUI RemoteInputView (b/67527720)
+public class RemoteInputView extends LinearLayout implements View.OnClickListener, TextWatcher {
+
+    private static final String TAG = "RemoteInput";
+
+    /**
+     * A marker object that let's us easily find views of this class.
+     */
+    public static final Object VIEW_TAG = new Object();
+
+    private RemoteEditText mEditText;
+    private ImageButton mSendButton;
+    private ProgressBar mProgressBar;
+    private PendingIntent mPendingIntent;
+    private RemoteInput[] mRemoteInputs;
+    private RemoteInput mRemoteInput;
+
+    private int mRevealCx;
+    private int mRevealCy;
+    private int mRevealR;
+    private boolean mResetting;
+
+    public RemoteInputView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mProgressBar = findViewById(R.id.remote_input_progress);
+        mSendButton = findViewById(R.id.remote_input_send);
+        mSendButton.setOnClickListener(this);
+
+        mEditText = (RemoteEditText) getChildAt(0);
+        mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+            @Override
+            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+                final boolean isSoftImeEvent = event == null
+                        && (actionId == EditorInfo.IME_ACTION_DONE
+                                || actionId == EditorInfo.IME_ACTION_NEXT
+                                || actionId == EditorInfo.IME_ACTION_SEND);
+                final boolean isKeyboardEnterKey = event != null
+                        && KeyEvent.isConfirmKey(event.getKeyCode())
+                        && event.getAction() == KeyEvent.ACTION_DOWN;
+
+                if (isSoftImeEvent || isKeyboardEnterKey) {
+                    if (mEditText.length() > 0) {
+                        sendRemoteInput();
+                    }
+                    // Consume action to prevent IME from closing.
+                    return true;
+                }
+                return false;
+            }
+        });
+        mEditText.addTextChangedListener(this);
+        mEditText.setInnerFocusable(false);
+        mEditText.mRemoteInputView = this;
+    }
+
+    private void sendRemoteInput() {
+        Bundle results = new Bundle();
+        results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
+        Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
+                results);
+
+        mEditText.setEnabled(false);
+        mSendButton.setVisibility(INVISIBLE);
+        mProgressBar.setVisibility(VISIBLE);
+        mEditText.mShowImeOnInputConnection = false;
+
+        // Tell ShortcutManager that this package has been "activated".  ShortcutManager
+        // will reset the throttling for this package.
+        // Strictly speaking, the intent receiver may be different from the intent creator,
+        // but that's an edge case, and also because we can't always know which package will receive
+        // an intent, so we just reset for the creator.
+        getContext().getSystemService(ShortcutManager.class).onApplicationActive(
+                mPendingIntent.getCreatorPackage(),
+                getContext().getUserId());
+
+        try {
+            mPendingIntent.send(mContext, 0, fillInIntent);
+            reset();
+        } catch (PendingIntent.CanceledException e) {
+            Log.i(TAG, "Unable to send remote input result", e);
+            Toast.makeText(mContext, "Failure sending pending intent for inline reply :(",
+                    Toast.LENGTH_SHORT).show();
+            reset();
+        }
+    }
+
+    /**
+     * Creates a remote input view.
+     */
+    public static RemoteInputView inflate(Context context, ViewGroup root) {
+        RemoteInputView v = (RemoteInputView) LayoutInflater.from(context).inflate(
+                R.layout.slice_remote_input, root, false);
+        v.setTag(VIEW_TAG);
+        return v;
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v == mSendButton) {
+            sendRemoteInput();
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        super.onTouchEvent(event);
+
+        // We never want for a touch to escape to an outer view or one we covered.
+        return true;
+    }
+
+    private void onDefocus() {
+        setVisibility(INVISIBLE);
+    }
+
+    /**
+     * Set the pending intent for remote input.
+     */
+    public void setPendingIntent(PendingIntent pendingIntent) {
+        mPendingIntent = pendingIntent;
+    }
+
+    /**
+     * Set the remote inputs for this view.
+     */
+    public void setRemoteInput(RemoteInput[] remoteInputs, RemoteInput remoteInput) {
+        mRemoteInputs = remoteInputs;
+        mRemoteInput = remoteInput;
+        mEditText.setHint(mRemoteInput.getLabel());
+    }
+
+    /**
+     * Focuses the remote input view.
+     */
+    public void focusAnimated() {
+        if (getVisibility() != VISIBLE) {
+            Animator animator = ViewAnimationUtils.createCircularReveal(
+                    this, mRevealCx, mRevealCy, 0, mRevealR);
+            animator.setDuration(200);
+            animator.start();
+        }
+        focus();
+    }
+
+    private void focus() {
+        setVisibility(VISIBLE);
+        mEditText.setInnerFocusable(true);
+        mEditText.mShowImeOnInputConnection = true;
+        mEditText.setSelection(mEditText.getText().length());
+        mEditText.requestFocus();
+        updateSendButton();
+    }
+
+    private void reset() {
+        mResetting = true;
+
+        mEditText.getText().clear();
+        mEditText.setEnabled(true);
+        mSendButton.setVisibility(VISIBLE);
+        mProgressBar.setVisibility(INVISIBLE);
+        updateSendButton();
+        onDefocus();
+
+        mResetting = false;
+    }
+
+    @Override
+    public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+        if (mResetting && child == mEditText) {
+            // Suppress text events if it happens during resetting. Ideally this would be
+            // suppressed by the text view not being shown, but that doesn't work here because it
+            // needs to stay visible for the animation.
+            return false;
+        }
+        return super.onRequestSendAccessibilityEvent(child, event);
+    }
+
+    private void updateSendButton() {
+        mSendButton.setEnabled(mEditText.getText().length() != 0);
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+    }
+
+    @Override
+    public void afterTextChanged(Editable s) {
+        updateSendButton();
+    }
+
+    /**
+     * Tries to find an action that matches the current pending intent of this view and updates its
+     * state to that of the found action
+     *
+     * @return true if a matching action was found, false otherwise
+     */
+    public boolean updatePendingIntentFromActions(Notification.Action[] actions) {
+        if (mPendingIntent == null || actions == null) {
+            return false;
+        }
+        Intent current = mPendingIntent.getIntent();
+        if (current == null) {
+            return false;
+        }
+
+        for (Notification.Action a : actions) {
+            RemoteInput[] inputs = a.getRemoteInputs();
+            if (a.actionIntent == null || inputs == null) {
+                continue;
+            }
+            Intent candidate = a.actionIntent.getIntent();
+            if (!current.filterEquals(candidate)) {
+                continue;
+            }
+
+            RemoteInput input = null;
+            for (RemoteInput i : inputs) {
+                if (i.getAllowFreeFormInput()) {
+                    input = i;
+                }
+            }
+            if (input == null) {
+                continue;
+            }
+            setPendingIntent(a.actionIntent);
+            setRemoteInput(inputs, input);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @hide
+     */
+    public void setRevealParameters(int cx, int cy, int r) {
+        mRevealCx = cx;
+        mRevealCy = cy;
+        mRevealR = r;
+    }
+
+    @Override
+    public void dispatchStartTemporaryDetach() {
+        super.dispatchStartTemporaryDetach();
+        // Detach the EditText temporarily such that it doesn't get onDetachedFromWindow and
+        // won't lose IME focus.
+        detachViewFromParent(mEditText);
+    }
+
+    @Override
+    public void dispatchFinishTemporaryDetach() {
+        if (isAttachedToWindow()) {
+            attachViewToParent(mEditText, 0, mEditText.getLayoutParams());
+        } else {
+            removeDetachedView(mEditText, false /* animate */);
+        }
+        super.dispatchFinishTemporaryDetach();
+    }
+
+    /**
+     * An EditText that changes appearance based on whether it's focusable and becomes un-focusable
+     * whenever the user navigates away from it or it becomes invisible.
+     */
+    public static class RemoteEditText extends EditText {
+
+        private final Drawable mBackground;
+        private RemoteInputView mRemoteInputView;
+        boolean mShowImeOnInputConnection;
+
+        public RemoteEditText(Context context, AttributeSet attrs) {
+            super(context, attrs);
+            mBackground = getBackground();
+        }
+
+        private void defocusIfNeeded(boolean animate) {
+            if (mRemoteInputView != null || isTemporarilyDetached()) {
+                if (isTemporarilyDetached()) {
+                    // We might get reattached but then the other one of HUN / expanded might steal
+                    // our focus, so we'll need to save our text here.
+                }
+                return;
+            }
+            if (isFocusable() && isEnabled()) {
+                setInnerFocusable(false);
+                if (mRemoteInputView != null) {
+                    mRemoteInputView.onDefocus();
+                }
+                mShowImeOnInputConnection = false;
+            }
+        }
+
+        @Override
+        protected void onVisibilityChanged(View changedView, int visibility) {
+            super.onVisibilityChanged(changedView, visibility);
+
+            if (!isShown()) {
+                defocusIfNeeded(false /* animate */);
+            }
+        }
+
+        @Override
+        protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+            super.onFocusChanged(focused, direction, previouslyFocusedRect);
+            if (!focused) {
+                defocusIfNeeded(true /* animate */);
+            }
+        }
+
+        @Override
+        public void getFocusedRect(Rect r) {
+            super.getFocusedRect(r);
+            r.top = mScrollY;
+            r.bottom = mScrollY + (mBottom - mTop);
+        }
+
+        @Override
+        public boolean onKeyDown(int keyCode, KeyEvent event) {
+            if (keyCode == KeyEvent.KEYCODE_BACK) {
+                // Eat the DOWN event here to prevent any default behavior.
+                return true;
+            }
+            return super.onKeyDown(keyCode, event);
+        }
+
+        @Override
+        public boolean onKeyUp(int keyCode, KeyEvent event) {
+            if (keyCode == KeyEvent.KEYCODE_BACK) {
+                defocusIfNeeded(true /* animate */);
+                return true;
+            }
+            return super.onKeyUp(keyCode, event);
+        }
+
+        @Override
+        public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+            final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
+
+            if (mShowImeOnInputConnection && inputConnection != null) {
+                final InputMethodManager imm = InputMethodManager.getInstance();
+                if (imm != null) {
+                    // onCreateInputConnection is called by InputMethodManager in the middle of
+                    // setting up the connection to the IME; wait with requesting the IME until that
+                    // work has completed.
+                    post(new Runnable() {
+                        @Override
+                        public void run() {
+                            imm.viewClicked(RemoteEditText.this);
+                            imm.showSoftInput(RemoteEditText.this, 0);
+                        }
+                    });
+                }
+            }
+
+            return inputConnection;
+        }
+
+        @Override
+        public void onCommitCompletion(CompletionInfo text) {
+            clearComposingText();
+            setText(text.getText());
+            setSelection(getText().length());
+        }
+
+        void setInnerFocusable(boolean focusable) {
+            setFocusableInTouchMode(focusable);
+            setFocusable(focusable);
+            setCursorVisible(focusable);
+
+            if (focusable) {
+                requestFocus();
+                setBackground(mBackground);
+            } else {
+                setBackground(null);
+            }
+
+        }
+    }
+}
diff --git a/core/java/android/slice/views/ShortcutView.java b/core/java/android/slice/views/ShortcutView.java
new file mode 100644
index 0000000..8fe2f1a
--- /dev/null
+++ b/core/java/android/slice/views/ShortcutView.java
@@ -0,0 +1,110 @@
+/*
+ * 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.slice.views;
+
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.OvalShape;
+import android.net.Uri;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.slice.views.SliceView.SliceModeView;
+import android.view.ViewGroup;
+
+import com.android.internal.R;
+
+/**
+ * @hide
+ */
+public class ShortcutView extends SliceModeView {
+
+    private static final String TAG = "ShortcutView";
+
+    private PendingIntent mAction;
+    private Uri mUri;
+    private int mLargeIconSize;
+    private int mSmallIconSize;
+
+    public ShortcutView(Context context) {
+        super(context);
+        mLargeIconSize = getContext().getResources()
+                .getDimensionPixelSize(R.dimen.slice_shortcut_size);
+        mSmallIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.slice_icon_size);
+        setLayoutParams(new ViewGroup.LayoutParams(mLargeIconSize, mLargeIconSize));
+    }
+
+    @Override
+    public void setSlice(Slice slice) {
+        removeAllViews();
+        SliceItem sliceItem = SliceQuery.find(slice, SliceItem.TYPE_ACTION);
+        SliceItem iconItem = slice.getPrimaryIcon();
+        SliceItem textItem = sliceItem != null
+                ? SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT)
+                : SliceQuery.find(slice, SliceItem.TYPE_TEXT);
+        SliceItem colorItem = sliceItem != null
+                ? SliceQuery.find(sliceItem, SliceItem.TYPE_COLOR)
+                : SliceQuery.find(slice, SliceItem.TYPE_COLOR);
+        if (colorItem == null) {
+            colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
+        }
+        // TODO: pick better default colour
+        final int color = colorItem != null ? colorItem.getColor() : Color.GRAY;
+        ShapeDrawable circle = new ShapeDrawable(new OvalShape());
+        circle.setTint(color);
+        setBackground(circle);
+        if (iconItem != null) {
+            final boolean isLarge = iconItem.hasHint(Slice.HINT_LARGE);
+            final int iconSize = isLarge ? mLargeIconSize : mSmallIconSize;
+            SliceViewUtil.createCircledIcon(getContext(), color, iconSize, iconItem.getIcon(),
+                    isLarge, this /* parent */);
+            mAction = sliceItem != null ? sliceItem.getAction()
+                    : null;
+            mUri = slice.getUri();
+            setClickable(true);
+        } else {
+            setClickable(false);
+        }
+    }
+
+    @Override
+    public String getMode() {
+        return SliceView.MODE_SHORTCUT;
+    }
+
+    @Override
+    public boolean performClick() {
+        if (!callOnClick()) {
+            try {
+                if (mAction != null) {
+                    mAction.send();
+                } else {
+                    Intent intent = new Intent(Intent.ACTION_VIEW).setData(mUri);
+                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    getContext().startActivity(intent);
+                }
+            } catch (CanceledException e) {
+                e.printStackTrace();
+            }
+        }
+        return true;
+    }
+}
diff --git a/core/java/android/slice/views/SliceView.java b/core/java/android/slice/views/SliceView.java
new file mode 100644
index 0000000..f379248
--- /dev/null
+++ b/core/java/android/slice/views/SliceView.java
@@ -0,0 +1,249 @@
+/*
+ * 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.slice.views;
+
+import android.annotation.StringDef;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+
+/**
+ * A view that can display a {@link Slice} in different {@link SliceMode}'s.
+ *
+ * @hide
+ */
+public class SliceView extends LinearLayout {
+
+    private static final String TAG = "SliceView";
+
+    /**
+     * @hide
+     */
+    public abstract static class SliceModeView extends FrameLayout {
+
+        public SliceModeView(Context context) {
+            super(context);
+        }
+
+        /**
+         * @return the {@link SliceMode} of the slice being presented.
+         */
+        public abstract String getMode();
+
+        /**
+         * @param slice the slice to show in this view.
+         */
+        public abstract void setSlice(Slice slice);
+    }
+
+    /**
+     * @hide
+     */
+    @StringDef({
+            MODE_SMALL, MODE_LARGE, MODE_SHORTCUT
+    })
+    public @interface SliceMode {}
+
+    /**
+     * Mode indicating this slice should be presented in small template format.
+     */
+    public static final String MODE_SMALL       = "SLICE_SMALL";
+    /**
+     * Mode indicating this slice should be presented in large template format.
+     */
+    public static final String MODE_LARGE       = "SLICE_LARGE";
+    /**
+     * Mode indicating this slice should be presented as an icon.
+     */
+    public static final String MODE_SHORTCUT    = "SLICE_ICON";
+
+    /**
+     * Will select the type of slice binding based on size of the View. TODO: Put in some info about
+     * that selection.
+     */
+    private static final String MODE_AUTO = "auto";
+
+    private String mMode = MODE_AUTO;
+    private SliceModeView mCurrentView;
+    private final ActionRow mActions;
+    private Slice mCurrentSlice;
+    private boolean mShowActions = true;
+
+    /**
+     * Simple constructor to create a slice view from code.
+     *
+     * @param context The context the view is running in.
+     */
+    public SliceView(Context context) {
+        super(context);
+        setOrientation(LinearLayout.VERTICAL);
+        mActions = new ActionRow(mContext, true);
+        mActions.setBackground(new ColorDrawable(0xffeeeeee));
+        mCurrentView = new LargeTemplateView(mContext);
+        addView(mCurrentView);
+        addView(mActions);
+    }
+
+    /**
+     * @hide
+     */
+    public void bindSlice(Intent intent) {
+        // TODO
+    }
+
+    /**
+     * Binds this view to the {@link Slice} associated with the provided {@link Uri}.
+     */
+    public void bindSlice(Uri sliceUri) {
+        validate(sliceUri);
+        Slice s = mContext.getContentResolver().bindSlice(sliceUri);
+        bindSlice(s);
+    }
+
+    /**
+     * Binds this view to the provided {@link Slice}.
+     */
+    public void bindSlice(Slice slice) {
+        mCurrentSlice = slice;
+        if (mCurrentSlice != null) {
+            reinflate();
+        }
+    }
+
+    /**
+     * Call to clean up the view.
+     */
+    public void unbindSlice() {
+        mCurrentSlice = null;
+    }
+
+    /**
+     * Set the {@link SliceMode} this view should present in.
+     */
+    public void setMode(@SliceMode String mode) {
+        setMode(mode, false /* animate */);
+    }
+
+    /**
+     * @hide
+     */
+    public void setMode(@SliceMode String mode, boolean animate) {
+        if (animate) {
+            Log.e(TAG, "Animation not supported yet");
+        }
+        mMode = mode;
+        reinflate();
+    }
+
+    /**
+     * @return the {@link SliceMode} this view is presenting in.
+     */
+    public @SliceMode String getMode() {
+        if (mMode.equals(MODE_AUTO)) {
+            return MODE_LARGE;
+        }
+        return mMode;
+    }
+
+    /**
+     * @hide
+     *
+     * Whether this view should show a row of actions with it.
+     */
+    public void setShowActionRow(boolean show) {
+        mShowActions = show;
+        reinflate();
+    }
+
+    private SliceModeView createView(String mode) {
+        switch (mode) {
+            case MODE_SHORTCUT:
+                return new ShortcutView(getContext());
+            case MODE_SMALL:
+                return new SmallTemplateView(getContext());
+        }
+        return new LargeTemplateView(getContext());
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        unbindSlice();
+    }
+
+    private void reinflate() {
+        if (mCurrentSlice == null) {
+            return;
+        }
+        // TODO: Smarter mapping here from one state to the next.
+        SliceItem color = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_COLOR);
+        SliceItem[] items = mCurrentSlice.getItems();
+        SliceItem actionRow = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_SLICE,
+                Slice.HINT_ACTIONS,
+                Slice.HINT_ALT);
+        String mode = getMode();
+        if (!mode.equals(mCurrentView.getMode())) {
+            removeAllViews();
+            mCurrentView = createView(mode);
+            addView(mCurrentView);
+            addView(mActions);
+        }
+        if (items.length > 1 || (items.length != 0 && items[0] != actionRow)) {
+            mCurrentView.setVisibility(View.VISIBLE);
+            mCurrentView.setSlice(mCurrentSlice);
+        } else {
+            mCurrentView.setVisibility(View.GONE);
+        }
+
+        boolean showActions = mShowActions && actionRow != null
+                && !mode.equals(MODE_SHORTCUT);
+        if (showActions) {
+            mActions.setActions(actionRow, color);
+            mActions.setVisibility(View.VISIBLE);
+        } else {
+            mActions.setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        // TODO -- may need to rethink for AGSA
+        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            requestDisallowInterceptTouchEvent(true);
+        }
+        return super.onInterceptTouchEvent(ev);
+    }
+
+    private static void validate(Uri sliceUri) {
+        if (!ContentResolver.SCHEME_SLICE.equals(sliceUri.getScheme())) {
+            throw new RuntimeException("Invalid uri " + sliceUri);
+        }
+        if (sliceUri.getPathSegments().size() == 0) {
+            throw new RuntimeException("Invalid uri " + sliceUri);
+        }
+    }
+}
diff --git a/core/java/android/slice/views/SliceViewUtil.java b/core/java/android/slice/views/SliceViewUtil.java
new file mode 100644
index 0000000..1b5a6d1
--- /dev/null
+++ b/core/java/android/slice/views/SliceViewUtil.java
@@ -0,0 +1,182 @@
+/*
+ * 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.slice.views;
+
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.view.Gravity;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+/**
+ * A bunch of utilities for slice UI.
+ *
+ * @hide
+ */
+public class SliceViewUtil {
+
+    /**
+     * @hide
+     */
+    @ColorInt
+    public static int getColorAccent(Context context) {
+        return getColorAttr(context, android.R.attr.colorAccent);
+    }
+
+    /**
+     * @hide
+     */
+    @ColorInt
+    public static int getColorError(Context context) {
+        return getColorAttr(context, android.R.attr.colorError);
+    }
+
+    /**
+     * @hide
+     */
+    @ColorInt
+    public static int getDefaultColor(Context context, int resId) {
+        final ColorStateList list = context.getResources().getColorStateList(resId,
+                context.getTheme());
+
+        return list.getDefaultColor();
+    }
+
+    /**
+     * @hide
+     */
+    @ColorInt
+    public static int getDisabled(Context context, int inputColor) {
+        return applyAlphaAttr(context, android.R.attr.disabledAlpha, inputColor);
+    }
+
+    /**
+     * @hide
+     */
+    @ColorInt
+    public static int applyAlphaAttr(Context context, int attr, int inputColor) {
+        TypedArray ta = context.obtainStyledAttributes(new int[] {
+                attr
+        });
+        float alpha = ta.getFloat(0, 0);
+        ta.recycle();
+        return applyAlpha(alpha, inputColor);
+    }
+
+    /**
+     * @hide
+     */
+    @ColorInt
+    public static int applyAlpha(float alpha, int inputColor) {
+        alpha *= Color.alpha(inputColor);
+        return Color.argb((int) (alpha), Color.red(inputColor), Color.green(inputColor),
+                Color.blue(inputColor));
+    }
+
+    /**
+     * @hide
+     */
+    @ColorInt
+    public static int getColorAttr(Context context, int attr) {
+        TypedArray ta = context.obtainStyledAttributes(new int[] {
+                attr
+        });
+        @ColorInt
+        int colorAccent = ta.getColor(0, 0);
+        ta.recycle();
+        return colorAccent;
+    }
+
+    /**
+     * @hide
+     */
+    public static int getThemeAttr(Context context, int attr) {
+        TypedArray ta = context.obtainStyledAttributes(new int[] {
+                attr
+        });
+        int theme = ta.getResourceId(0, 0);
+        ta.recycle();
+        return theme;
+    }
+
+    /**
+     * @hide
+     */
+    public static Drawable getDrawable(Context context, int attr) {
+        TypedArray ta = context.obtainStyledAttributes(new int[] {
+                attr
+        });
+        Drawable drawable = ta.getDrawable(0);
+        ta.recycle();
+        return drawable;
+    }
+
+    /**
+     * @hide
+     */
+    public static void createCircledIcon(Context context, int color, int iconSize, Icon icon,
+            boolean isLarge, ViewGroup parent) {
+        ImageView v = new ImageView(context);
+        v.setImageIcon(icon);
+        parent.addView(v);
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
+        if (isLarge) {
+            // XXX better way to convert from icon -> bitmap or crop an icon (?)
+            Bitmap iconBm = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
+            Canvas iconCanvas = new Canvas(iconBm);
+            v.layout(0, 0, iconSize, iconSize);
+            v.draw(iconCanvas);
+            v.setImageBitmap(getCircularBitmap(iconBm));
+        } else {
+            v.setColorFilter(Color.WHITE);
+        }
+        lp.width = iconSize;
+        lp.height = iconSize;
+        lp.gravity = Gravity.CENTER;
+    }
+
+    /**
+     * @hide
+     */
+    public static Bitmap getCircularBitmap(Bitmap bitmap) {
+        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
+                bitmap.getHeight(), Config.ARGB_8888);
+        Canvas canvas = new Canvas(output);
+        final Paint paint = new Paint();
+        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+        paint.setAntiAlias(true);
+        canvas.drawARGB(0, 0, 0, 0);
+        canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2,
+                bitmap.getWidth() / 2, paint);
+        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+        canvas.drawBitmap(bitmap, rect, rect, paint);
+        return output;
+    }
+}
diff --git a/core/java/android/slice/views/SmallTemplateView.java b/core/java/android/slice/views/SmallTemplateView.java
new file mode 100644
index 0000000..b0b181e
--- /dev/null
+++ b/core/java/android/slice/views/SmallTemplateView.java
@@ -0,0 +1,211 @@
+/*
+ * 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.slice.views;
+
+import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.slice.views.LargeSliceAdapter.SliceListView;
+import android.slice.views.SliceView.SliceModeView;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+import java.text.Format;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Small template is also used to construct list items for use with {@link LargeTemplateView}.
+ *
+ * @hide
+ */
+public class SmallTemplateView extends SliceModeView implements SliceListView {
+
+    private static final String TAG = "SmallTemplateView";
+
+    private int mIconSize;
+    private int mPadding;
+
+    private LinearLayout mStartContainer;
+    private TextView mTitleText;
+    private TextView mSecondaryText;
+    private LinearLayout mEndContainer;
+
+    public SmallTemplateView(Context context) {
+        super(context);
+        inflate(context, R.layout.slice_small_template, this);
+        mIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.slice_icon_size);
+        mPadding = getContext().getResources().getDimensionPixelSize(R.dimen.slice_padding);
+
+        mStartContainer = (LinearLayout) findViewById(android.R.id.icon_frame);
+        mTitleText = (TextView) findViewById(android.R.id.title);
+        mSecondaryText = (TextView) findViewById(android.R.id.summary);
+        mEndContainer = (LinearLayout) findViewById(android.R.id.widget_frame);
+    }
+
+    @Override
+    public String getMode() {
+        return SliceView.MODE_SMALL;
+    }
+
+    @Override
+    public void setSliceItem(SliceItem slice) {
+        resetViews();
+        SliceItem colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
+        int color = colorItem != null ? colorItem.getColor() : -1;
+
+        // Look for any title elements
+        List<SliceItem> titleItems = SliceQuery.findAll(slice, -1, Slice.HINT_TITLE,
+                null);
+        boolean hasTitleText = false;
+        boolean hasTitleItem = false;
+        for (int i = 0; i < titleItems.size(); i++) {
+            SliceItem item = titleItems.get(i);
+            if (!hasTitleItem) {
+                // icon, action icon, or timestamp
+                if (item.getType() == SliceItem.TYPE_ACTION) {
+                    hasTitleItem = addIcon(item, color, mStartContainer);
+                } else if (item.getType() == SliceItem.TYPE_IMAGE) {
+                    addIcon(item, color, mStartContainer);
+                    hasTitleItem = true;
+                } else if (item.getType() == SliceItem.TYPE_TIMESTAMP) {
+                    TextView tv = new TextView(getContext());
+                    tv.setText(convertTimeToString(item.getTimestamp()));
+                    hasTitleItem = true;
+                }
+            }
+            if (!hasTitleText && item.getType() == SliceItem.TYPE_TEXT) {
+                mTitleText.setText(item.getText());
+                hasTitleText = true;
+            }
+            if (hasTitleText && hasTitleItem) {
+                break;
+            }
+        }
+        mTitleText.setVisibility(hasTitleText ? View.VISIBLE : View.GONE);
+        mStartContainer.setVisibility(hasTitleItem ? View.VISIBLE : View.GONE);
+
+        if (slice.getType() != SliceItem.TYPE_SLICE) {
+            return;
+        }
+
+        // Deal with remaining items
+        int itemCount = 0;
+        boolean hasSummary = false;
+        ArrayList<SliceItem> sliceItems = new ArrayList<SliceItem>(
+                Arrays.asList(slice.getSlice().getItems()));
+        for (int i = 0; i < sliceItems.size(); i++) {
+            SliceItem item = sliceItems.get(i);
+            if (!hasSummary && item.getType() == SliceItem.TYPE_TEXT
+                    && !item.hasHint(Slice.HINT_TITLE)) {
+                // TODO -- Should combine all text items?
+                mSecondaryText.setText(item.getText());
+                hasSummary = true;
+            }
+            if (itemCount <= 3) {
+                if (item.getType() == SliceItem.TYPE_ACTION) {
+                    if (addIcon(item, color, mEndContainer)) {
+                        itemCount++;
+                    }
+                } else if (item.getType() == SliceItem.TYPE_IMAGE) {
+                    addIcon(item, color, mEndContainer);
+                    itemCount++;
+                } else if (item.getType() == SliceItem.TYPE_TIMESTAMP) {
+                    TextView tv = new TextView(getContext());
+                    tv.setText(convertTimeToString(item.getTimestamp()));
+                    mEndContainer.addView(tv);
+                    itemCount++;
+                } else if (item.getType() == SliceItem.TYPE_SLICE) {
+                    SliceItem[] subItems = item.getSlice().getItems();
+                    for (int j = 0; j < subItems.length; j++) {
+                        sliceItems.add(subItems[j]);
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
+    public void setSlice(Slice slice) {
+        setSliceItem(new SliceItem(slice, SliceItem.TYPE_SLICE, slice.getHints()));
+    }
+
+    /**
+     * @return Whether an icon was added.
+     */
+    private boolean addIcon(SliceItem sliceItem, int color, LinearLayout container) {
+        SliceItem image = null;
+        SliceItem action = null;
+        if (sliceItem.getType() == SliceItem.TYPE_ACTION) {
+            image = SliceQuery.find(sliceItem.getSlice(), SliceItem.TYPE_IMAGE);
+            action = sliceItem;
+        } else if (sliceItem.getType() == SliceItem.TYPE_IMAGE) {
+            image = sliceItem;
+        }
+        if (image != null) {
+            ImageView iv = new ImageView(getContext());
+            iv.setImageIcon(image.getIcon());
+            if (action != null) {
+                final SliceItem sliceAction = action;
+                iv.setOnClickListener(v -> AsyncTask.execute(
+                        () -> {
+                            try {
+                                sliceAction.getAction().send();
+                            } catch (CanceledException e) {
+                                e.printStackTrace();
+                            }
+                        }));
+                iv.setBackground(SliceViewUtil.getDrawable(getContext(),
+                        android.R.attr.selectableItemBackground));
+            }
+            if (color != -1 && !sliceItem.hasHint(Slice.HINT_NO_TINT)) {
+                iv.setColorFilter(color);
+            }
+            container.addView(iv);
+            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) iv.getLayoutParams();
+            lp.width = mIconSize;
+            lp.height = mIconSize;
+            lp.setMarginStart(mPadding);
+            return true;
+        }
+        return false;
+    }
+
+    private String convertTimeToString(long time) {
+        // TODO -- figure out what format(s) we support
+        Date date = new Date(time);
+        Format format = new SimpleDateFormat("MM dd yyyy HH:mm:ss");
+        return format.format(date);
+    }
+
+    private void resetViews() {
+        mStartContainer.removeAllViews();
+        mEndContainer.removeAllViews();
+        mTitleText.setText(null);
+        mSecondaryText.setText(null);
+    }
+}
diff --git a/core/java/android/widget/SmartSelectSprite.java b/core/java/android/widget/SmartSelectSprite.java
index 7cbc494b..a391c6e 100644
--- a/core/java/android/widget/SmartSelectSprite.java
+++ b/core/java/android/widget/SmartSelectSprite.java
@@ -206,7 +206,7 @@
 
             if (mRectangleBorderType == RectangleBorderType.OVERSHOOT) {
                 mDrawRect.left -= cornerRadius / 2;
-                mDrawRect.right -= cornerRadius / 2;
+                mDrawRect.right += cornerRadius / 2;
             } else {
                 switch (mExpansionDirection) {
                     case ExpansionDirection.CENTER:
@@ -437,6 +437,7 @@
         RectangleWithTextSelectionLayout centerRectangle = null;
 
         int startingOffset = 0;
+        int startingRectangleIndex = 0;
         for (int index = 0; index < rectangleCount; ++index) {
             final RectangleWithTextSelectionLayout rectangleWithTextSelectionLayout =
                     destinationRectangles.get(index);
@@ -446,6 +447,7 @@
                 break;
             }
             startingOffset += rectangle.width();
+            ++startingRectangleIndex;
         }
 
         if (centerRectangle == null) {
@@ -454,10 +456,6 @@
 
         startingOffset += start.x - centerRectangle.getRectangle().left;
 
-        final float centerRectangleHalfHeight = centerRectangle.getRectangle().height() / 2;
-        final float startingOffsetLeft = startingOffset - centerRectangleHalfHeight;
-        final float startingOffsetRight = startingOffset + centerRectangleHalfHeight;
-
         final @RoundedRectangleShape.ExpansionDirection int[] expansionDirections =
                 generateDirections(centerRectangle, destinationRectangles);
 
@@ -482,6 +480,30 @@
         final RectangleList rectangleList = new RectangleList(shapes);
         final ShapeDrawable shapeDrawable = new ShapeDrawable(rectangleList);
 
+        final float startingOffsetLeft;
+        final float startingOffsetRight;
+
+        final RoundedRectangleShape startingRectangleShape = shapes.get(startingRectangleIndex);
+        final float cornerRadius = startingRectangleShape.getCornerRadius();
+        if (startingRectangleShape.mRectangleBorderType
+                == RoundedRectangleShape.RectangleBorderType.FIT) {
+            switch (startingRectangleShape.mExpansionDirection) {
+                case RoundedRectangleShape.ExpansionDirection.LEFT:
+                    startingOffsetLeft = startingOffsetRight = startingOffset - cornerRadius / 2;
+                    break;
+                case RoundedRectangleShape.ExpansionDirection.RIGHT:
+                    startingOffsetLeft = startingOffsetRight = startingOffset + cornerRadius / 2;
+                    break;
+                case RoundedRectangleShape.ExpansionDirection.CENTER:  // fall through
+                default:
+                    startingOffsetLeft = startingOffset - cornerRadius / 2;
+                    startingOffsetRight = startingOffset + cornerRadius / 2;
+                    break;
+            }
+        } else {
+            startingOffsetLeft = startingOffsetRight = startingOffset;
+        }
+
         final Paint paint = shapeDrawable.getPaint();
         paint.setColor(mStrokeColor);
         paint.setStyle(Paint.Style.STROKE);
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index f085e29..15dc6f5 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -143,6 +143,9 @@
     public static boolean checkWifiOnly(Context context) {
         ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
+        if (cm == null) {
+            return false;
+        }
         return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
     }
 
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 82eb1ab..03603e4 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -58,10 +58,12 @@
     void onNotificationError(String pkg, String tag, int id,
             int uid, int initialPid, String message, int userId);
     void onClearAllNotifications(int userId);
-    void onNotificationClear(String pkg, String tag, int id, int userId);
+    void onNotificationClear(String pkg, String tag, int id, int userId, String key, int dismissalSurface);
     void onNotificationVisibilityChanged( in NotificationVisibility[] newlyVisibleKeys,
             in NotificationVisibility[] noLongerVisibleKeys);
     void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
+    void onNotificationDirectReplied(String key);
+    void onNotificationSettingsViewed(String key);
     void setSystemUiVisibility(int vis, int mask, String cause);
 
     void onGlobalActionsShown();
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index ad05a51..635eed3 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1051,7 +1051,7 @@
     }
 
     // Read the bitmap blob.
-    size_t size = bitmap->getSize();
+    size_t size = bitmap->computeByteSize();
     android::Parcel::ReadableBlob blob;
     android::status_t status = p->readBlob(size, &blob);
     if (status) {
@@ -1188,7 +1188,7 @@
             p->allowFds() ? "allowed" : "forbidden");
 #endif
 
-    size_t size = bitmap.getSize();
+    size_t size = bitmap.computeByteSize();
     android::Parcel::WritableBlob blob;
     status = p->writeBlob(size, mutableCopy, &blob);
     if (status) {
@@ -1411,7 +1411,7 @@
         android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
 
         // the java side has already checked that buffer is large enough
-        memcpy(abp.pointer(), src, bitmap.getSize());
+        memcpy(abp.pointer(), src, bitmap.computeByteSize());
     }
 }
 
@@ -1424,7 +1424,7 @@
     if (NULL != dst) {
         android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
         // the java side has already checked that buffer is large enough
-        memcpy(dst, abp.pointer(), bitmap.getSize());
+        memcpy(dst, abp.pointer(), bitmap.computeByteSize());
         bitmap.notifyPixelsChanged();
     }
 }
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 64e12b4..5990d7b 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -174,13 +174,12 @@
             return false;
         }
 
-        const int64_t size64 = info.getSafeSize64(bitmap->rowBytes());
-        if (!sk_64_isS32(size64)) {
+        const size_t size = info.computeByteSize(bitmap->rowBytes());
+        if (size > SK_MaxS32) {
             ALOGW("bitmap is too large");
             return false;
         }
 
-        const size_t size = sk_64_asS32(size64);
         if (size > mSize) {
             ALOGW("bitmap marked for reuse (%u bytes) can't fit new bitmap "
                   "(%zu bytes)", mSize, size);
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 5ea501e..90cc7bb 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -645,7 +645,7 @@
     const int maxHeight = SkTMax(bitmap->height(), mRecycledBitmap->info().height());
     const SkImageInfo maxInfo = bitmap->info().makeWH(maxWidth, maxHeight);
     const size_t rowBytes = maxInfo.minRowBytes();
-    const size_t bytesNeeded = maxInfo.getSafeSize(rowBytes);
+    const size_t bytesNeeded = maxInfo.computeByteSize(rowBytes);
     if (bytesNeeded <= mRecycledBytes) {
         // Here we take advantage of reconfigure() to reset the rowBytes
         // of mRecycledBitmap.  It is very important that we pass in
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index e4540c0..d5f2a5c 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -30,14 +30,14 @@
 
 static jlong Typeface_createFromTypeface(JNIEnv* env, jobject, jlong familyHandle, jint style) {
     Typeface* family = reinterpret_cast<Typeface*>(familyHandle);
-    Typeface* face = Typeface::createRelative(family, (SkTypeface::Style)style);
+    Typeface* face = Typeface::createRelative(family, (Typeface::Style)style);
     // TODO: the following logic shouldn't be necessary, the above should always succeed.
     // Try to find the closest matching font, using the standard heuristic
     if (NULL == face) {
-        face = Typeface::createRelative(family, (SkTypeface::Style)(style ^ SkTypeface::kItalic));
+        face = Typeface::createRelative(family, (Typeface::Style)(style ^ Typeface::kItalic));
     }
     for (int i = 0; NULL == face && i < 4; i++) {
-        face = Typeface::createRelative(family, (SkTypeface::Style)i);
+        face = Typeface::createRelative(family, (Typeface::Style)i);
     }
     return reinterpret_cast<jlong>(face);
 }
@@ -78,7 +78,7 @@
 
 static jint Typeface_getStyle(JNIEnv* env, jobject obj, jlong faceHandle) {
     Typeface* face = reinterpret_cast<Typeface*>(faceHandle);
-    return face->fSkiaStyle;
+    return face->fAPIStyle;
 }
 
 static jint Typeface_getWeight(JNIEnv* env, jobject obj, jlong faceHandle) {
diff --git a/core/jni/android_app_admin_SecurityLog.cpp b/core/jni/android_app_admin_SecurityLog.cpp
index 5c45b4b..b3bcaa0 100644
--- a/core/jni/android_app_admin_SecurityLog.cpp
+++ b/core/jni/android_app_admin_SecurityLog.cpp
@@ -14,183 +14,26 @@
  * limitations under the License.
  */
 
-#include <fcntl.h>
-
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-#include "jni.h"
+#include <log/log_id.h>
 #include <private/android_logger.h>
 
-// The size of the tag number comes out of the payload size.
-#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+
+#include "core_jni_helpers.h"
+#include "eventlog_helper.h"
 
 namespace android {
 
-static jclass gCollectionClass;
-static jmethodID gCollectionAddID;
-
-static jclass gEventClass;
-static jmethodID gEventInitID;
-
-static jclass gIntegerClass;
-static jfieldID gIntegerValueID;
-
-static jclass gLongClass;
-static jfieldID gLongValueID;
-
-static jclass gFloatClass;
-static jfieldID gFloatValueID;
-
-static jclass gStringClass;
-
+constexpr char kSecurityLogEventClass[] = "android/app/admin/SecurityLog$SecurityEvent";
+template class EventLogHelper<log_id_t::LOG_ID_SECURITY, kSecurityLogEventClass>;
+using SLog = EventLogHelper<log_id_t::LOG_ID_SECURITY, kSecurityLogEventClass>;
 
 static jboolean android_app_admin_SecurityLog_isLoggingEnabled(JNIEnv* env,
                                                     jobject /* clazz */) {
     return (bool)__android_log_security();
 }
 
-static jint android_app_admin_SecurityLog_writeEvent_String(JNIEnv* env,
-                                                    jobject /* clazz */,
-                                                    jint tag, jstring value) {
-    uint8_t buf[MAX_EVENT_PAYLOAD];
-
-    // Don't throw NPE -- I feel like it's sort of mean for a logging function
-    // to be all crashy if you pass in NULL -- but make the NULL value explicit.
-    const char *str = value != NULL ? env->GetStringUTFChars(value, NULL) : "NULL";
-    uint32_t len = strlen(str);
-    size_t max = sizeof(buf) - sizeof(len) - 2;  // Type byte, final newline
-    if (len > max) len = max;
-
-    buf[0] = EVENT_TYPE_STRING;
-    memcpy(&buf[1], &len, sizeof(len));
-    memcpy(&buf[1 + sizeof(len)], str, len);
-    buf[1 + sizeof(len) + len] = '\n';
-
-    if (value != NULL) env->ReleaseStringUTFChars(value, str);
-    return __android_log_security_bwrite(tag, buf, 2 + sizeof(len) + len);
-}
-
-static jint android_app_admin_SecurityLog_writeEvent_Array(JNIEnv* env, jobject clazz,
-                                                   jint tag, jobjectArray value) {
-    if (value == NULL) {
-        return android_app_admin_SecurityLog_writeEvent_String(env, clazz, tag, NULL);
-    }
-
-    uint8_t buf[MAX_EVENT_PAYLOAD];
-    const size_t max = sizeof(buf) - 1;  // leave room for final newline
-    size_t pos = 2;  // Save room for type tag & array count
-
-    jsize copied = 0, num = env->GetArrayLength(value);
-    for (; copied < num && copied < 255; ++copied) {
-        jobject item = env->GetObjectArrayElement(value, copied);
-        if (item == NULL || env->IsInstanceOf(item, gStringClass)) {
-            if (pos + 1 + sizeof(jint) > max) break;
-            const char *str = item != NULL ? env->GetStringUTFChars((jstring) item, NULL) : "NULL";
-            jint len = strlen(str);
-            if (pos + 1 + sizeof(len) + len > max) len = max - pos - 1 - sizeof(len);
-            buf[pos++] = EVENT_TYPE_STRING;
-            memcpy(&buf[pos], &len, sizeof(len));
-            memcpy(&buf[pos + sizeof(len)], str, len);
-            pos += sizeof(len) + len;
-            if (item != NULL) env->ReleaseStringUTFChars((jstring) item, str);
-        } else if (env->IsInstanceOf(item, gIntegerClass)) {
-            jint intVal = env->GetIntField(item, gIntegerValueID);
-            if (pos + 1 + sizeof(intVal) > max) break;
-            buf[pos++] = EVENT_TYPE_INT;
-            memcpy(&buf[pos], &intVal, sizeof(intVal));
-            pos += sizeof(intVal);
-        } else if (env->IsInstanceOf(item, gLongClass)) {
-            jlong longVal = env->GetLongField(item, gLongValueID);
-            if (pos + 1 + sizeof(longVal) > max) break;
-            buf[pos++] = EVENT_TYPE_LONG;
-            memcpy(&buf[pos], &longVal, sizeof(longVal));
-            pos += sizeof(longVal);
-        } else if (env->IsInstanceOf(item, gFloatClass)) {
-            jfloat floatVal = env->GetFloatField(item, gFloatValueID);
-            if (pos + 1 + sizeof(floatVal) > max) break;
-            buf[pos++] = EVENT_TYPE_FLOAT;
-            memcpy(&buf[pos], &floatVal, sizeof(floatVal));
-            pos += sizeof(floatVal);
-        } else {
-            jniThrowException(env,
-                    "java/lang/IllegalArgumentException",
-                    "Invalid payload item type");
-            return -1;
-        }
-        env->DeleteLocalRef(item);
-    }
-
-    buf[0] = EVENT_TYPE_LIST;
-    buf[1] = copied;
-    buf[pos++] = '\n';
-    return __android_log_security_bwrite(tag, buf, pos);
-}
-
-static void readEvents(JNIEnv* env, int loggerMode, jlong startTime, jobject out) {
-    struct logger_list *logger_list;
-    if (startTime) {
-        logger_list = android_logger_list_alloc_time(loggerMode,
-                log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0);
-    } else {
-        logger_list = android_logger_list_alloc(loggerMode, 0, 0);
-    }
-    if (!logger_list) {
-        jniThrowIOException(env, errno);
-        return;
-    }
-
-    if (!android_logger_open(logger_list, LOG_ID_SECURITY)) {
-        jniThrowIOException(env, errno);
-        android_logger_list_free(logger_list);
-        return;
-    }
-
-    while (1) {
-        log_msg log_msg;
-        int ret = android_logger_list_read(logger_list, &log_msg);
-
-        if (ret == 0) {
-            break;
-        }
-        if (ret < 0) {
-            if (ret == -EINTR) {
-                continue;
-            }
-            if (ret == -EINVAL) {
-                jniThrowException(env, "java/io/IOException", "Event too short");
-            } else if (ret != -EAGAIN) {
-                jniThrowIOException(env, -ret);  // Will throw on return
-            }
-            break;
-        }
-
-        if (log_msg.id() != LOG_ID_SECURITY) {
-            continue;
-        }
-
-        jsize len = ret;
-        jbyteArray array = env->NewByteArray(len);
-        if (array == NULL) {
-            break;
-        }
-
-        jbyte *bytes = env->GetByteArrayElements(array, NULL);
-        memcpy(bytes, log_msg.buf, len);
-        env->ReleaseByteArrayElements(array, bytes, 0);
-
-        jobject event = env->NewObject(gEventClass, gEventInitID, array);
-        if (event == NULL) {
-            break;
-        }
-
-        env->CallBooleanMethod(out, gCollectionAddID, event);
-        env->DeleteLocalRef(event);
-        env->DeleteLocalRef(array);
-    }
-
-    android_logger_list_close(logger_list);
-}
-
 static void android_app_admin_SecurityLog_readEvents(JNIEnv* env, jobject /* clazz */,
                                              jobject out) {
 
@@ -198,7 +41,7 @@
         jniThrowNullPointerException(env, NULL);
         return;
     }
-    readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, out);
+    SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, out);
 }
 
 static void android_app_admin_SecurityLog_readEventsSince(JNIEnv* env, jobject /* clazz */,
@@ -209,7 +52,7 @@
         jniThrowNullPointerException(env, NULL);
         return;
     }
-    readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, timestamp, out);
+    SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, timestamp, out);
 }
 
 static void android_app_admin_SecurityLog_readPreviousEvents(JNIEnv* env, jobject /* clazz */,
@@ -219,7 +62,7 @@
         jniThrowNullPointerException(env, NULL);
         return;
     }
-    readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_PSTORE, 0, out);
+    SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_PSTORE, 0, out);
 }
 
 static void android_app_admin_SecurityLog_readEventsOnWrapping(JNIEnv* env, jobject /* clazz */,
@@ -229,7 +72,8 @@
         jniThrowNullPointerException(env, NULL);
         return;
     }
-    readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, timestamp, out);
+    SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, timestamp,
+            out);
 }
 
 /*
@@ -243,11 +87,11 @@
     },
     { "writeEvent",
       "(ILjava/lang/String;)I",
-      (void*) android_app_admin_SecurityLog_writeEvent_String
+      (void*) SLog::writeEventString
     },
     { "writeEvent",
       "(I[Ljava/lang/Object;)I",
-      (void*) android_app_admin_SecurityLog_writeEvent_Array
+      (void*) SLog::writeEventArray
     },
     { "readEvents",
       "(Ljava/util/Collection;)V",
@@ -267,41 +111,8 @@
     },
 };
 
-static struct { const char *name; jclass *clazz; } gClasses[] = {
-    { "android/app/admin/SecurityLog$SecurityEvent", &gEventClass },
-    { "java/lang/Integer", &gIntegerClass },
-    { "java/lang/Long", &gLongClass },
-    { "java/lang/Float", &gFloatClass },
-    { "java/lang/String", &gStringClass },
-    { "java/util/Collection", &gCollectionClass },
-};
-
-static struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = {
-    { &gIntegerClass, "value", "I", &gIntegerValueID },
-    { &gLongClass, "value", "J", &gLongValueID },
-    { &gFloatClass, "value", "F", &gFloatValueID },
-};
-
-static struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = {
-    { &gEventClass, "<init>", "([B)V", &gEventInitID },
-    { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID },
-};
-
 int register_android_app_admin_SecurityLog(JNIEnv* env) {
-    for (int i = 0; i < NELEM(gClasses); ++i) {
-        jclass clazz = FindClassOrDie(env, gClasses[i].name);
-        *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz);
-    }
-
-    for (int i = 0; i < NELEM(gFields); ++i) {
-        *gFields[i].id = GetFieldIDOrDie(env,
-                *gFields[i].c, gFields[i].name, gFields[i].ft);
-    }
-
-    for (int i = 0; i < NELEM(gMethods); ++i) {
-        *gMethods[i].id = GetMethodIDOrDie(env,
-                *gMethods[i].c, gMethods[i].name, gMethods[i].mt);
-    }
+    SLog::Init(env);
 
     return RegisterMethodsOrDie(
             env,
diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp
index 9fd7c40..3b5a144 100644
--- a/core/jni/android_util_EventLog.cpp
+++ b/core/jni/android_util_EventLog.cpp
@@ -14,214 +14,20 @@
  * limitations under the License.
  */
 
-#include <fcntl.h>
-
-#include <log/log_event_list.h>
-
-#include <log/log.h>
+#include <android-base/macros.h>
+#include <log/log_id.h>
 
 #include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
 #include "jni.h"
 
-#define UNUSED  __attribute__((__unused__))
+#include "core_jni_helpers.h"
+#include "eventlog_helper.h"
 
 namespace android {
 
-static jclass gCollectionClass;
-static jmethodID gCollectionAddID;
-
-static jclass gEventClass;
-static jmethodID gEventInitID;
-
-static jclass gIntegerClass;
-static jfieldID gIntegerValueID;
-
-static jclass gLongClass;
-static jfieldID gLongValueID;
-
-static jclass gFloatClass;
-static jfieldID gFloatValueID;
-
-static jclass gStringClass;
-
-/*
- * In class android.util.EventLog:
- *  static native int writeEvent(int tag, int value)
- */
-static jint android_util_EventLog_writeEvent_Integer(JNIEnv* env UNUSED,
-                                                     jobject clazz UNUSED,
-                                                     jint tag, jint value)
-{
-    android_log_event_list ctx(tag);
-    ctx << (int32_t)value;
-    return ctx.write();
-}
-
-/*
- * In class android.util.EventLog:
- *  static native int writeEvent(long tag, long value)
- */
-static jint android_util_EventLog_writeEvent_Long(JNIEnv* env UNUSED,
-                                                  jobject clazz UNUSED,
-                                                  jint tag, jlong value)
-{
-    android_log_event_list ctx(tag);
-    ctx << (int64_t)value;
-    return ctx.write();
-}
-
-/*
- * In class android.util.EventLog:
- *  static native int writeEvent(long tag, float value)
- */
-static jint android_util_EventLog_writeEvent_Float(JNIEnv* env UNUSED,
-                                                  jobject clazz UNUSED,
-                                                  jint tag, jfloat value)
-{
-    android_log_event_list ctx(tag);
-    ctx << (float)value;
-    return ctx.write();
-}
-
-/*
- * In class android.util.EventLog:
- *  static native int writeEvent(int tag, String value)
- */
-static jint android_util_EventLog_writeEvent_String(JNIEnv* env,
-                                                    jobject clazz UNUSED,
-                                                    jint tag, jstring value) {
-    android_log_event_list ctx(tag);
-    // Don't throw NPE -- I feel like it's sort of mean for a logging function
-    // to be all crashy if you pass in NULL -- but make the NULL value explicit.
-    if (value != NULL) {
-        const char *str = env->GetStringUTFChars(value, NULL);
-        ctx << str;
-        env->ReleaseStringUTFChars(value, str);
-    } else {
-        ctx << "NULL";
-    }
-    return ctx.write();
-}
-
-/*
- * In class android.util.EventLog:
- *  static native int writeEvent(long tag, Object... value)
- */
-static jint android_util_EventLog_writeEvent_Array(JNIEnv* env, jobject clazz,
-                                                   jint tag, jobjectArray value) {
-    android_log_event_list ctx(tag);
-
-    if (value == NULL) {
-        ctx << "[NULL]";
-        return ctx.write();
-    }
-
-    jsize copied = 0, num = env->GetArrayLength(value);
-    for (; copied < num && copied < 255; ++copied) {
-        if (ctx.status()) break;
-        jobject item = env->GetObjectArrayElement(value, copied);
-        if (item == NULL) {
-            ctx << "NULL";
-        } else if (env->IsInstanceOf(item, gStringClass)) {
-            const char *str = env->GetStringUTFChars((jstring) item, NULL);
-            ctx << str;
-            env->ReleaseStringUTFChars((jstring) item, str);
-        } else if (env->IsInstanceOf(item, gIntegerClass)) {
-            ctx << (int32_t)env->GetIntField(item, gIntegerValueID);
-        } else if (env->IsInstanceOf(item, gLongClass)) {
-            ctx << (int64_t)env->GetLongField(item, gLongValueID);
-        } else if (env->IsInstanceOf(item, gFloatClass)) {
-            ctx << (float)env->GetFloatField(item, gFloatValueID);
-        } else {
-            jniThrowException(env,
-                    "java/lang/IllegalArgumentException",
-                    "Invalid payload item type");
-            return -1;
-        }
-        env->DeleteLocalRef(item);
-    }
-    return ctx.write();
-}
-
-static void readEvents(JNIEnv* env, int loggerMode, jintArray tags, jlong startTime, jobject out) {
-    struct logger_list *logger_list;
-    if (startTime) {
-        logger_list = android_logger_list_alloc_time(loggerMode,
-                log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0);
-    } else {
-        logger_list = android_logger_list_alloc(loggerMode, 0, 0);
-    }
-    if (!logger_list) {
-        jniThrowIOException(env, errno);
-        return;
-    }
-
-    if (!android_logger_open(logger_list, LOG_ID_EVENTS)) {
-        jniThrowIOException(env, errno);
-        android_logger_list_free(logger_list);
-        return;
-    }
-
-    jsize tagLength = env->GetArrayLength(tags);
-    jint *tagValues = env->GetIntArrayElements(tags, NULL);
-
-    while (1) {
-        log_msg log_msg;
-        int ret = android_logger_list_read(logger_list, &log_msg);
-
-        if (ret == 0) {
-            break;
-        }
-        if (ret < 0) {
-            if (ret == -EINTR) {
-                continue;
-            }
-            if (ret == -EINVAL) {
-                jniThrowException(env, "java/io/IOException", "Event too short");
-            } else if (ret != -EAGAIN) {
-                jniThrowIOException(env, -ret);  // Will throw on return
-            }
-            break;
-        }
-
-        if (log_msg.id() != LOG_ID_EVENTS) {
-            continue;
-        }
-
-        int32_t tag = * (int32_t *) log_msg.msg();
-
-        int found = 0;
-        for (int i = 0; !found && i < tagLength; ++i) {
-            found = (tag == tagValues[i]);
-        }
-
-        if (found) {
-            jsize len = ret;
-            jbyteArray array = env->NewByteArray(len);
-            if (array == NULL) {
-                break;
-            }
-
-            jbyte *bytes = env->GetByteArrayElements(array, NULL);
-            memcpy(bytes, log_msg.buf, len);
-            env->ReleaseByteArrayElements(array, bytes, 0);
-
-            jobject event = env->NewObject(gEventClass, gEventInitID, array);
-            if (event == NULL) {
-                break;
-            }
-
-            env->CallBooleanMethod(out, gCollectionAddID, event);
-            env->DeleteLocalRef(event);
-            env->DeleteLocalRef(array);
-        }
-    }
-
-    android_logger_list_close(logger_list);
-
-    env->ReleaseIntArrayElements(tags, tagValues, 0);
-}
+constexpr char kEventLogEventClass[] = "android/util/EventLog$Event";
+template class EventLogHelper<log_id_t::LOG_ID_EVENTS, kEventLogEventClass>;
+using ELog = EventLogHelper<log_id_t::LOG_ID_EVENTS, kEventLogEventClass>;
 
 /*
  * In class android.util.EventLog:
@@ -229,7 +35,7 @@
  *
  *  Reads events from the event log
  */
-static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz UNUSED,
+static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED,
                                              jintArray tags,
                                              jobject out) {
 
@@ -238,7 +44,7 @@
         return;
     }
 
-    readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tags, 0, out);
+    ELog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tags, 0, out);
  }
 /*
  * In class android.util.EventLog:
@@ -246,7 +52,7 @@
  *
  *  Reads events from the event log, blocking until events after timestamp are to be overwritten.
  */
-static void android_util_EventLog_readEventsOnWrapping(JNIEnv* env, jobject clazz UNUSED,
+static void android_util_EventLog_readEventsOnWrapping(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED,
                                              jintArray tags,
                                              jlong timestamp,
                                              jobject out) {
@@ -254,8 +60,8 @@
         jniThrowNullPointerException(env, NULL);
         return;
     }
-    readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP,
-            tags, timestamp, out);
+    ELog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, tags,
+            timestamp, out);
 }
 
 /*
@@ -263,17 +69,11 @@
  */
 static const JNINativeMethod gRegisterMethods[] = {
     /* name, signature, funcPtr */
-    { "writeEvent", "(II)I", (void*) android_util_EventLog_writeEvent_Integer },
-    { "writeEvent", "(IJ)I", (void*) android_util_EventLog_writeEvent_Long },
-    { "writeEvent", "(IF)I", (void*) android_util_EventLog_writeEvent_Float },
-    { "writeEvent",
-      "(ILjava/lang/String;)I",
-      (void*) android_util_EventLog_writeEvent_String
-    },
-    { "writeEvent",
-      "(I[Ljava/lang/Object;)I",
-      (void*) android_util_EventLog_writeEvent_Array
-    },
+    { "writeEvent", "(II)I", (void*) ELog::writeEventInteger },
+    { "writeEvent", "(IJ)I", (void*) ELog::writeEventLong },
+    { "writeEvent", "(IF)I", (void*) ELog::writeEventFloat },
+    { "writeEvent", "(ILjava/lang/String;)I", (void*) ELog::writeEventString },
+    { "writeEvent", "(I[Ljava/lang/Object;)I", (void*) ELog::writeEventArray },
     { "readEvents",
       "([ILjava/util/Collection;)V",
       (void*) android_util_EventLog_readEvents
@@ -284,41 +84,8 @@
     },
 };
 
-static struct { const char *name; jclass *clazz; } gClasses[] = {
-    { "android/util/EventLog$Event", &gEventClass },
-    { "java/lang/Integer", &gIntegerClass },
-    { "java/lang/Long", &gLongClass },
-    { "java/lang/Float", &gFloatClass },
-    { "java/lang/String", &gStringClass },
-    { "java/util/Collection", &gCollectionClass },
-};
-
-static struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = {
-    { &gIntegerClass, "value", "I", &gIntegerValueID },
-    { &gLongClass, "value", "J", &gLongValueID },
-    { &gFloatClass, "value", "F", &gFloatValueID },
-};
-
-static struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = {
-    { &gEventClass, "<init>", "([B)V", &gEventInitID },
-    { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID },
-};
-
 int register_android_util_EventLog(JNIEnv* env) {
-    for (int i = 0; i < NELEM(gClasses); ++i) {
-        jclass clazz = FindClassOrDie(env, gClasses[i].name);
-        *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz);
-    }
-
-    for (int i = 0; i < NELEM(gFields); ++i) {
-        *gFields[i].id = GetFieldIDOrDie(env,
-                *gFields[i].c, gFields[i].name, gFields[i].ft);
-    }
-
-    for (int i = 0; i < NELEM(gMethods); ++i) {
-        *gMethods[i].id = GetMethodIDOrDie(env,
-                *gMethods[i].c, gMethods[i].name, gMethods[i].mt);
-    }
+    ELog::Init(env);
 
     return RegisterMethodsOrDie(
             env,
diff --git a/core/jni/eventlog_helper.h b/core/jni/eventlog_helper.h
new file mode 100644
index 0000000..1101b83
--- /dev/null
+++ b/core/jni/eventlog_helper.h
@@ -0,0 +1,285 @@
+/*
+ * 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.
+ */
+
+#ifndef FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_
+#define FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_
+
+#include <fcntl.h>
+
+#include <android-base/macros.h>
+#include <log/log_event_list.h>
+
+#include <log/log.h>
+
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include "core_jni_helpers.h"
+#include "jni.h"
+
+namespace android {
+
+template <log_id_t LogID, const char* EventClassDescriptor>
+class EventLogHelper {
+public:
+    static void Init(JNIEnv* env) {
+        struct { const char *name; jclass *clazz; } gClasses[] = {
+                { EventClassDescriptor, &gEventClass },
+                { "java/lang/Integer", &gIntegerClass },
+                { "java/lang/Long", &gLongClass },
+                { "java/lang/Float", &gFloatClass },
+                { "java/lang/String", &gStringClass },
+                { "java/util/Collection", &gCollectionClass },
+        };
+        struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = {
+                { &gIntegerClass, "value", "I", &gIntegerValueID },
+                { &gLongClass, "value", "J", &gLongValueID },
+                { &gFloatClass, "value", "F", &gFloatValueID },
+        };
+        struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = {
+                { &gEventClass, "<init>", "([B)V", &gEventInitID },
+                { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID },
+        };
+
+        for (size_t i = 0; i < NELEM(gClasses); ++i) {
+            ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, gClasses[i].name));
+            *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz.get());
+        }
+        for (size_t i = 0; i < NELEM(gFields); ++i) {
+            *gFields[i].id = GetFieldIDOrDie(env,
+                    *gFields[i].c, gFields[i].name, gFields[i].ft);
+        }
+
+        for (size_t i = 0; i < NELEM(gMethods); ++i) {
+            *gMethods[i].id = GetMethodIDOrDie(env,
+                    *gMethods[i].c, gMethods[i].name, gMethods[i].mt);
+        }
+    }
+
+    static jint writeEventInteger(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED,
+            jint tag, jint value) {
+        android_log_event_list ctx(tag);
+        ctx << (int32_t)value;
+        return ctx.write(LogID);
+    }
+    static jint writeEventLong(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED,
+            jint tag, jlong value) {
+        android_log_event_list ctx(tag);
+        ctx << (int64_t)value;
+        return ctx.write(LogID);
+    }
+    static jint writeEventFloat(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED,
+            jint tag, jfloat value) {
+        android_log_event_list ctx(tag);
+        ctx << (float)value;
+        return ctx.write(LogID);
+    }
+    static jint writeEventString(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag,
+            jstring value) {
+        android_log_event_list ctx(tag);
+        // Don't throw NPE -- I feel like it's sort of mean for a logging function
+        // to be all crashy if you pass in NULL -- but make the NULL value explicit.
+        if (value != NULL) {
+            const char *str = env->GetStringUTFChars(value, NULL);
+            ctx << str;
+            env->ReleaseStringUTFChars(value, str);
+        } else {
+            ctx << "NULL";
+        }
+        return ctx.write(LogID);
+    }
+    static jint writeEventArray(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag,
+            jobjectArray value) {
+        android_log_event_list ctx(tag);
+
+        if (value == NULL) {
+            ctx << "[NULL]";
+            return ctx.write(LogID);
+        }
+
+        jsize copied = 0, num = env->GetArrayLength(value);
+        for (; copied < num && copied < 255; ++copied) {
+            if (ctx.status()) break;
+            jobject item = env->GetObjectArrayElement(value, copied);
+            if (item == NULL) {
+                ctx << "NULL";
+            } else if (env->IsInstanceOf(item, gStringClass)) {
+                const char *str = env->GetStringUTFChars((jstring) item, NULL);
+                ctx << str;
+                env->ReleaseStringUTFChars((jstring) item, str);
+            } else if (env->IsInstanceOf(item, gIntegerClass)) {
+                ctx << (int32_t)env->GetIntField(item, gIntegerValueID);
+            } else if (env->IsInstanceOf(item, gLongClass)) {
+                ctx << (int64_t)env->GetLongField(item, gLongValueID);
+            } else if (env->IsInstanceOf(item, gFloatClass)) {
+                ctx << (float)env->GetFloatField(item, gFloatValueID);
+            } else {
+                jniThrowException(env,
+                        "java/lang/IllegalArgumentException",
+                        "Invalid payload item type");
+                return -1;
+            }
+            env->DeleteLocalRef(item);
+        }
+        return ctx.write(LogID);
+    }
+
+    static void readEvents(JNIEnv* env, int loggerMode, jlong startTime, jobject out) {
+        readEvents(env, loggerMode, nullptr, startTime, out);
+    }
+
+    static void readEvents(JNIEnv* env, int loggerMode, jintArray tags, jlong startTime,
+            jobject out) {
+        struct logger_list *logger_list;
+        if (startTime) {
+            logger_list = android_logger_list_alloc_time(loggerMode,
+                    log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0);
+        } else {
+            logger_list = android_logger_list_alloc(loggerMode, 0, 0);
+        }
+        if (!logger_list) {
+            jniThrowIOException(env, errno);
+            return;
+        }
+
+        if (!android_logger_open(logger_list, LogID)) {
+            jniThrowIOException(env, errno);
+            android_logger_list_free(logger_list);
+            return;
+        }
+
+        jsize tagLength = 0;
+        jint *tagValues = nullptr;
+        if (tags != nullptr) {
+            tagLength = env->GetArrayLength(tags);
+            tagValues = env->GetIntArrayElements(tags, NULL);
+        }
+
+        while (1) {
+            log_msg log_msg;
+            int ret = android_logger_list_read(logger_list, &log_msg);
+
+            if (ret == 0) {
+                break;
+            }
+            if (ret < 0) {
+                if (ret == -EINTR) {
+                    continue;
+                }
+                if (ret == -EINVAL) {
+                    jniThrowException(env, "java/io/IOException", "Event too short");
+                } else if (ret != -EAGAIN) {
+                    jniThrowIOException(env, -ret);  // Will throw on return
+                }
+                break;
+            }
+
+            if (log_msg.id() != LogID) {
+                continue;
+            }
+
+            int32_t tag = * (int32_t *) log_msg.msg();
+
+            if (tags != nullptr) {
+                bool found = false;
+                for (int i = 0; !found && i < tagLength; ++i) {
+                    found = (tag == tagValues[i]);
+                }
+                if (!found) {
+                    continue;
+                }
+            }
+
+            jsize len = ret;
+            jbyteArray array = env->NewByteArray(len);
+            if (array == NULL) {
+                break;
+            }
+
+            jbyte *bytes = env->GetByteArrayElements(array, NULL);
+            memcpy(bytes, log_msg.buf, len);
+            env->ReleaseByteArrayElements(array, bytes, 0);
+
+            jobject event = env->NewObject(gEventClass, gEventInitID, array);
+            if (event == NULL) {
+                break;
+            }
+
+            env->CallBooleanMethod(out, gCollectionAddID, event);
+            env->DeleteLocalRef(event);
+            env->DeleteLocalRef(array);
+            if (env->ExceptionCheck() == JNI_TRUE) {
+                break;
+            }
+        }
+
+        android_logger_list_close(logger_list);
+
+        if (tags != nullptr) {
+            env->ReleaseIntArrayElements(tags, tagValues, 0);
+        }
+    }
+
+private:
+    static jclass gCollectionClass;
+    static jmethodID gCollectionAddID;
+
+    static jclass gEventClass;
+    static jmethodID gEventInitID;
+
+    static jclass gIntegerClass;
+    static jfieldID gIntegerValueID;
+
+    static jclass gLongClass;
+    static jfieldID gLongValueID;
+
+    static jclass gFloatClass;
+    static jfieldID gFloatValueID;
+
+    static jclass gStringClass;
+};
+
+// Explicit instantiation declarations.
+template <log_id_t LogID, const char* EventClassDescriptor>
+jclass EventLogHelper<LogID, EventClassDescriptor>::gCollectionClass;
+template <log_id_t LogID, const char* EventClassDescriptor>
+jmethodID EventLogHelper<LogID, EventClassDescriptor>::gCollectionAddID;
+
+template <log_id_t LogID, const char* EventClassDescriptor>
+jclass EventLogHelper<LogID, EventClassDescriptor>::gEventClass;
+template <log_id_t LogID, const char* EventClassDescriptor>
+jmethodID EventLogHelper<LogID, EventClassDescriptor>::gEventInitID;
+
+template <log_id_t LogID, const char* EventClassDescriptor>
+jclass EventLogHelper<LogID, EventClassDescriptor>::gIntegerClass;
+template <log_id_t LogID, const char* EventClassDescriptor>
+jfieldID EventLogHelper<LogID, EventClassDescriptor>::gIntegerValueID;
+
+template <log_id_t LogID, const char* EventClassDescriptor>
+jclass EventLogHelper<LogID, EventClassDescriptor>::gLongClass;
+template <log_id_t LogID, const char* EventClassDescriptor>
+jfieldID EventLogHelper<LogID, EventClassDescriptor>::gLongValueID;
+
+template <log_id_t LogID, const char* EventClassDescriptor>
+jclass EventLogHelper<LogID, EventClassDescriptor>::gFloatClass;
+template <log_id_t LogID, const char* EventClassDescriptor>
+jfieldID EventLogHelper<LogID, EventClassDescriptor>::gFloatValueID;
+
+template <log_id_t LogID, const char* EventClassDescriptor>
+jclass EventLogHelper<LogID, EventClassDescriptor>::gStringClass;
+
+}  // namespace android
+
+#endif  // FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
new file mode 100644
index 0000000..86e31d0
--- /dev/null
+++ b/core/proto/android/os/batterystats.proto
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+option java_multiple_files = true;
+
+package android.os;
+
+message BatteryStatsProto {
+  int32 report_version = 1;
+  int64 parcel_version = 2;
+  string start_platform_version = 3;
+  string end_platform_version = 4;
+  BatteryHistoryProto history = 5;
+  repeated UidProto uids = 6;
+  SystemProto system = 7;
+}
+
+message BatteryHistoryProto {
+}
+
+message ControllerActivityProto {
+  // Time (milliseconds) spent in the idle state.
+  int64 idle_duration_ms = 1;
+  // Time (milliseconds) spent in the receive state.
+  int64 rx_duration_ms = 2;
+  // Total power (mAh) consumed by the controller in all states. The value may
+  // always be 0 if the device doesn't support power calculations.
+  int64 power_mah = 3;
+
+  // Represents a transmit level, where each level may draw a different amount
+  // of power. The levels themselves are controller-specific (and may possibly
+  // be device specific...yet to be confirmed).
+  message TxLevel {
+    // Transmit level. Higher levels draw more power.
+    int32 level = 1;
+    // Time spent in this specific transmit level state.
+    int64 duration_ms = 2;
+  }
+  repeated TxLevel tx = 4;
+}
+
+message SystemProto {
+}
+
+message TimerProto {
+  int64 duration_ms = 1;
+  int64 count = 2;
+}
+
+message UidProto {
+  // Combination of app ID and user ID.
+  int32 uid = 1;
+  repeated string package_names = 2;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index c3ceb1c..b3a606f 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -23,6 +23,7 @@
 import "frameworks/base/libs/incident/proto/android/section.proto";
 import "frameworks/base/core/proto/android/service/appwidget.proto";
 import "frameworks/base/core/proto/android/service/battery.proto";
+import "frameworks/base/core/proto/android/service/batterystats.proto";
 import "frameworks/base/core/proto/android/service/fingerprint.proto";
 import "frameworks/base/core/proto/android/service/diskstats.proto";
 import "frameworks/base/core/proto/android/service/netstats.proto";
@@ -84,6 +85,11 @@
         (section).args = "notification --proto"
     ];
 
+    android.service.batterystats.BatteryStatsServiceDumpProto batterystats = 3005 [
+        (section).type = SECTION_DUMPSYS,
+        (section).args = "batterystats --proto"
+    ];
+
     android.service.battery.BatteryServiceDumpProto battery = 3006 [
         (section).type = SECTION_DUMPSYS,
         (section).args = "battery --proto"
diff --git a/core/proto/android/service/batterystats.proto b/core/proto/android/service/batterystats.proto
new file mode 100644
index 0000000..4e989b7
--- /dev/null
+++ b/core/proto/android/service/batterystats.proto
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.service.batterystats;
+
+option java_multiple_files = true;
+option java_outer_classname = "BatteryStatsServiceProto";
+
+import "frameworks/base/core/proto/android/os/batterystats.proto";
+
+message BatteryStatsServiceDumpProto {
+  android.os.BatteryStatsProto batterystats = 1;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6a9afbc..ff579a6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -963,7 +963,7 @@
         android:permissionGroup="android.permission-group.MICROPHONE"
         android:label="@string/permlab_recordAudio"
         android:description="@string/permdesc_recordAudio"
-        android:protectionLevel="dangerous"/>
+        android:protectionLevel="dangerous|instant"/>
 
     <!-- ====================================================================== -->
     <!-- Permissions for accessing the UCE Service                              -->
diff --git a/core/res/res/drawable/ic_slice_send.xml b/core/res/res/drawable/ic_slice_send.xml
new file mode 100644
index 0000000..b8b6954a7
--- /dev/null
+++ b/core/res/res/drawable/ic_slice_send.xml
@@ -0,0 +1,24 @@
+<!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:autoMirrored="true"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4.02,42.0L46.0,24.0 4.02,6.0 4.0,20.0l30.0,4.0 -30.0,4.0z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/slice_remote_input_bg.xml b/core/res/res/drawable/slice_remote_input_bg.xml
new file mode 100644
index 0000000..3120679
--- /dev/null
+++ b/core/res/res/drawable/slice_remote_input_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#ff6c6c6c" />
+    <corners
+        android:bottomRightRadius="16dp"
+        android:bottomLeftRadius="16dp"/>
+</shape>
diff --git a/core/res/res/drawable/slice_ripple_drawable.xml b/core/res/res/drawable/slice_ripple_drawable.xml
new file mode 100644
index 0000000..5ba1f07
--- /dev/null
+++ b/core/res/res/drawable/slice_ripple_drawable.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2014 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
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight" />
\ No newline at end of file
diff --git a/core/res/res/layout/slice_grid.xml b/core/res/res/layout/slice_grid.xml
new file mode 100644
index 0000000..70df76b
--- /dev/null
+++ b/core/res/res/layout/slice_grid.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<android.slice.views.GridView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:background="?android:attr/activatedBackgroundIndicator"
+    android:clipToPadding="false">
+</android.slice.views.GridView>
diff --git a/core/res/res/layout/slice_message.xml b/core/res/res/layout/slice_message.xml
new file mode 100644
index 0000000..a3279b6
--- /dev/null
+++ b/core/res/res/layout/slice_message.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<android.slice.views.MessageView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingTop="12dp"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/activatedBackgroundIndicator"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:id="@android:id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="-4dp"
+        android:gravity="start|center_vertical"
+        android:orientation="horizontal"
+        android:paddingEnd="12dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp">
+        <!-- TODO: Support text source -->
+        <ImageView
+            android:id="@android:id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxWidth="48dp"
+            android:maxHeight="48dp" />
+    </LinearLayout>
+
+    <TextView android:id="@android:id/summary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignStart="@android:id/title"
+        android:textAppearance="?android:attr/textAppearanceListItem"
+        android:maxLines="10" />
+</android.slice.views.MessageView>
diff --git a/core/res/res/layout/slice_message_local.xml b/core/res/res/layout/slice_message_local.xml
new file mode 100644
index 0000000..d4180f3
--- /dev/null
+++ b/core/res/res/layout/slice_message_local.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<android.slice.views.MessageView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical|end"
+    android:paddingTop="12dp"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/activatedBackgroundIndicator"
+    android:clipToPadding="false">
+
+    <TextView android:id="@android:id/summary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignStart="@android:id/title"
+        android:layout_gravity="end"
+        android:gravity="end"
+        android:padding="8dp"
+        android:textAppearance="?android:attr/textAppearanceListItem"
+        android:background="#ffeeeeee"
+        android:maxLines="10" />
+
+</android.slice.views.MessageView>
diff --git a/core/res/res/layout/slice_remote_input.xml b/core/res/res/layout/slice_remote_input.xml
new file mode 100644
index 0000000..dc570c4
--- /dev/null
+++ b/core/res/res/layout/slice_remote_input.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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.
+-->
+<!-- LinearLayout -->
+<android.slice.views.RemoteInputView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/remote_input"
+        android:background="@drawable/slice_remote_input_bg"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent">
+
+    <view class="com.android.internal.slice.view.RemoteInputView$RemoteEditText"
+            android:id="@+id/remote_input_text"
+            android:layout_height="match_parent"
+            android:layout_width="0dp"
+            android:layout_weight="1"
+            android:paddingTop="2dp"
+            android:paddingBottom="4dp"
+            android:paddingStart="16dp"
+            android:paddingEnd="12dp"
+            android:gravity="start|center_vertical"
+            android:textAppearance="?android:attr/textAppearance"
+            android:textColor="#FFFFFFFF"
+            android:textColorHint="#99ffffff"
+            android:textSize="16sp"
+            android:background="@null"
+            android:singleLine="true"
+            android:ellipsize="start"
+            android:inputType="textShortMessage|textAutoCorrect|textCapSentences"
+            android:imeOptions="actionSend|flagNoExtractUi|flagNoFullscreen" />
+
+    <FrameLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_gravity="center_vertical">
+
+        <ImageButton
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:paddingStart="12dp"
+                android:paddingEnd="24dp"
+                android:paddingTop="16dp"
+                android:paddingBottom="16dp"
+                android:id="@+id/remote_input_send"
+                android:src="@drawable/ic_slice_send"
+                android:tint="#FFFFFF"
+                android:tintMode="src_in"
+                android:background="@drawable/slice_ripple_drawable" />
+
+        <ProgressBar
+                android:id="@+id/remote_input_progress"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:layout_marginEnd="6dp"
+                android:layout_gravity="center"
+                android:visibility="invisible"
+                android:indeterminate="true"
+                style="?android:attr/progressBarStyleSmall" />
+
+    </FrameLayout>
+
+</android.slice.views.RemoteInputView>
\ No newline at end of file
diff --git a/core/res/res/layout/slice_secondary_text.xml b/core/res/res/layout/slice_secondary_text.xml
new file mode 100644
index 0000000..80a1574
--- /dev/null
+++ b/core/res/res/layout/slice_secondary_text.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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.
+-->
+<!-- LinearLayout -->
+<TextView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textColor="?android:attr/textColorSecondary"
+        android:gravity="center"
+        android:layout_height="wrap_content"
+        android:padding="4dp"
+        android:layout_width="match_parent" />
diff --git a/core/res/res/layout/slice_small_template.xml b/core/res/res/layout/slice_small_template.xml
new file mode 100644
index 0000000..cced42b
--- /dev/null
+++ b/core/res/res/layout/slice_small_template.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:gravity="center_vertical"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/activatedBackgroundIndicator"
+    android:clipToPadding="false">
+
+    <LinearLayout
+        android:id="@android:id/icon_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="-4dp"
+        android:gravity="start|center_vertical"
+        android:orientation="horizontal"
+        android:paddingEnd="12dp"
+        android:paddingTop="4dp"
+        android:paddingBottom="4dp"/>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:gravity="center_vertical"
+        android:orientation="vertical">
+
+        <TextView android:id="@android:id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="2"
+            android:textAppearance="?android:attr/textAppearanceListItem" />
+
+        <TextView android:id="@android:id/summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignStart="@android:id/title"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+            android:textColor="?android:attr/textColorSecondary"
+            android:maxLines="10" />
+
+    </LinearLayout>
+
+    <LinearLayout android:id="@android:id/widget_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="end|center_vertical"
+        android:orientation="horizontal" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/slice_title.xml b/core/res/res/layout/slice_title.xml
new file mode 100644
index 0000000..455d59f
--- /dev/null
+++ b/core/res/res/layout/slice_title.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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.
+-->
+
+<!-- LinearLayout -->
+<TextView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?android:attr/textColorPrimary"
+        android:gravity="center"
+        android:layout_height="wrap_content"
+        android:padding="4dp"
+        android:layout_width="match_parent" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 9e4184b..5d6f04f 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Werkprofiel is weens ontbrekende administrasieprogram uitgevee"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Die werkprofiel se administrasieprogram ontbreek of is korrup. Gevolglik is jou werkprofiel en verwante data uitgevee. Kontak jou administrateur vir bystand."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Jou werkprofiel is nie meer op hierdie toestel beskikbaar nie"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Toestel word bestuur"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Jou organisasie bestuur hierdie toestel en kan netwerkverkeer monitor. Tik vir besonderhede."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Jou toestel sal uitgevee word"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Foon"</string>
     <string name="map" msgid="6068210738233985748">"Kaarte"</string>
     <string name="browse" msgid="6993590095938149861">"Blaaier"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontak"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Bergingspasie word min"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sommige stelselfunksies werk moontlik nie"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nie genoeg berging vir die stelsel nie. Maak seker jy het 250 MB spasie beskikbaar en herbegin."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 00257ee..ffae89c 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"በጎደለ የአስተዳዳሪ መተግበሪያ ምክንያት የሥራ መገለጫ ተሰርዟል።"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"የሥራ መገለጫ አስተዳዳሪ መተግበሪያው ወይም ይጎድላል ወይም ተበላሽቷል። በዚህ ምክንያት የሥራ መገለጫዎ እና ተዛማጅ ውሂብ ተሰርዘዋል። እርዳታን ለማግኘት አስተዳዳሪዎን ያነጋግሩ።"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"የሥራ መገለጫዎ ከዚህ በኋላ በዚህ መሣሪያ ላይ አይገኝም"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"በጣም ብዙ የይለፍ ቃል ሙከራዎች"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"መሣሪያው የሚተዳደር ነው"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"የእርስዎ ድርጅት ይህን መሣሪያ ያስተዳድራል፣ እና የአውታረ መረብ ትራፊክን ሊከታተል ይችላል። ዝርዝሮችን ለማግኘት መታ ያድርጉ።"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"የእርስዎ መሣሪያ ይደመሰሳል"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"ስልክ"</string>
     <string name="map" msgid="6068210738233985748">"ካርታዎች"</string>
     <string name="browse" msgid="6993590095938149861">"አሳሽ"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"ኤስኤምኤስ"</string>
+    <string name="add_contact" msgid="7990645816259405444">"ዕውቂያ"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"የማከማቻ ቦታ እያለቀ ነው"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"አንዳንድ የስርዓት ተግባራት ላይሰሩ ይችላሉ"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ለስርዓቱ የሚሆን በቂ ቦታ የለም። 250 ሜባ ነጻ ቦታ እንዳለዎት ያረጋግጡና ዳግም ያስጀምሩ።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 8f78cfa..4b20e74 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -180,6 +180,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"تم حذف الملف الشخصي للعمل نتيجة فقد تطبيق المشرف"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"تطبيق المشرف للملف الشخصي للعمل مفقود أو تالف لذا تم حذف الملف الشخصي للعمل والبيانات ذات الصلة. اتصل بالمشرف للحصول على المساعدة."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"لم يعد ملفك الشخصي للعمل متاحًا على هذا الجهاز"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"تتم إدارة الجهاز"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"تدير مؤسستك هذا الجهاز ويمكنها مراقبة حركة بيانات الشبكة. يمكنك النقر للحصول على تفاصيل."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"سيتم محو بيانات جهازك."</string>
@@ -1060,10 +1062,8 @@
     <string name="dial" msgid="4204975095406423102">"الهاتف"</string>
     <string name="map" msgid="6068210738233985748">"الخرائط"</string>
     <string name="browse" msgid="6993590095938149861">"المتصفح"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"‏رسالة SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"جهة اتصال"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"مساحة التخزين منخفضة"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"قد لا تعمل بعض وظائف النظام"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ليست هناك سعة تخزينية كافية للنظام. تأكد من أنه لديك مساحة خالية تبلغ ٢٥٠ ميغابايت وأعد التشغيل."</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 97a67e0..7ff8d04 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Admin tətbiqi olmadığından iş profili silindi"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"İş profili admin tətbiqi ya yoxdur, ya da korlanıb. Nəticədə iş profili və onunla bağlı data silinib. Kömək üçün admin ilə əlaqə saxlayın."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"İş profili artıq bu cihazda əlçatan deyil"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Həddindən çox parol cəhdi"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Cihaz idarə olunur"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Təşkilat bu cihazı idarə edir və şəbəkənin ötürülməsinə nəzarət edə bilər. Detallar üçün klikləyin."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Cihazınız təmizlənəcəkdir"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefon"</string>
     <string name="map" msgid="6068210738233985748">"Xəritə"</string>
     <string name="browse" msgid="6993590095938149861">"Brauzer"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Yaddaş yeri bitir"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bəzi sistem funksiyaları işləməyə bilər"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistem üçün yetərincə yaddaş ehtiyatı yoxdur. 250 MB yaddaş ehtiyatının olmasına əmin olun və yenidən başladın."</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 2daf942..bc1e5ab 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -174,6 +174,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil za Work je izbrisan jer nedostaje aplikacija za administratore"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplikacija za administratore na profilu za Work nedostaje ili je oštećena. Zbog toga su profil za Work i povezani podaci izbrisani. Obratite se administratoru za pomoć."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Profil za Work više nije dostupan na ovom uređaju"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Uređajem se upravlja"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Organizacija upravlja ovim uređajem i može da nadgleda mrežni saobraćaj. Dodirnite za detalje."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Uređaj će biti obrisan"</string>
@@ -1000,10 +1002,8 @@
     <string name="dial" msgid="4204975095406423102">"Pozovi"</string>
     <string name="map" msgid="6068210738233985748">"Mape"</string>
     <string name="browse" msgid="6993590095938149861">"Pregledač"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Memorijski prostor je na izmaku"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke sistemske funkcije možda ne funkcionišu"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nema dovoljno memorijskog prostora za sistem. Uverite se da imate 250 MB slobodnog prostora i ponovo pokrenite."</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index c89987b..b643078 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -176,6 +176,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Працоўны профіль выдалены з-за адсутнасці праграмы адміністратара"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Праграма адміністратара для працоўнага профілю адсутнічае або пашкоджана. У выніку гэтага ваш працоўны профіль і звязаныя з ім даныя былі выдалены. Звярніцеся па дапамогу да адміністратара."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Ваш працоўны профіль больш не даступны на гэтай прыладзе"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Прылада знаходзіцца пад кіраваннем"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Ваша арганізацыя кіруе гэтай прыладай і можа сачыць за сеткавым трафікам. Дакраніцеся для атрымання дадатковай інфармацыі."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Даныя вашай прылады будуць сцерты"</string>
@@ -1020,10 +1022,8 @@
     <string name="dial" msgid="4204975095406423102">"Тэлефон"</string>
     <string name="map" msgid="6068210738233985748">"Карты"</string>
     <string name="browse" msgid="6993590095938149861">"Браўзер"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Кантакт"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Месца для захавання на зыходзе"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некаторыя сістэмныя функцыі могуць не працаваць"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Не хапае сховішча для сістэмы. Пераканайцеся, што ў вас ёсць 250 МБ свабоднага месца, і перазапусціце."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 07bdc3d..44df773 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Служебният потребителски профил е изтрит поради липса на приложение за администриране"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Приложението за администриране на служебния потребителски профил липсва или е повредено. В резултат на това той и свързаните с него данни са изтрити. За съдействие се свържете с администратора си."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Служебният ви потребителски профил вече не е налице на това устройство"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Опитите за паролата са твърде много"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Устройството се управлява"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Организацията ви управлява това устройство и може да наблюдава мрежовия трафик. Докоснете за подробности."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Данните на устройството ви ще бъдат изтрити"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Телефон"</string>
     <string name="map" msgid="6068210738233985748">"Карти"</string>
     <string name="browse" msgid="6993590095938149861">"Браузър"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Контакт"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Мястото в хранилището е на изчерпване"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Възможно е някои функции на системата да не работят"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"За системата няма достатъчно място в хранилището. Уверете се, че имате свободни 250 МБ, и рестартирайте."</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 5d19486..76a6b0e 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"প্রশাসক অ্যাপ না থাকায় কর্মস্থলের প্রোফাইল মুছে ফেলা হয়েছে"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"কর্মস্থলের প্রোফাইলের প্রশাসক অ্যাপটি হয় নেই, অথবা সেটি ক্ষতিগ্রস্ত হয়েছে৷ এর ফলে আপনার কর্মস্থলের প্রোফাইল এবং সম্পর্কিত ডেটা মুছে ফেলা হয়েছে৷ সহায়তার জন্য আপনার প্রশাসকের সাথে যোগাযোগ করুন৷"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"আপনার কর্মস্থলের প্রোফাইলটি আর এই ডিভাইসে নেই"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"ডিভাইসটি পরিচালনা করা হচ্ছে"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"আপনার প্রতিষ্ঠান এই ডিভাইসটি পরিচালনা করে এবং এটির নেটওয়ার্ক ট্রাফিকের উপরে নজর রাখতে পারে। বিশদ বিবরণের জন্য ট্যাপ করুন।,"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"আপনার ডিভাইসটি মুছে ফেলা হবে"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"ফোন করুন"</string>
     <string name="map" msgid="6068210738233985748">"মানচিত্র"</string>
     <string name="browse" msgid="6993590095938149861">"ব্রাউজার"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"এসএমএস পাঠান"</string>
+    <string name="add_contact" msgid="7990645816259405444">"পরিচিতি যোগ করুন"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"স্টোরেজ পূর্ণ হতে চলেছে"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"কিছু কিছু সিস্টেম ক্রিয়াকলাপ কাজ নাও করতে পারে"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"সিস্টেমের জন্য যথেষ্ট স্টোরেজ নেই৷ আপনার কাছে ২৫০এমবি ফাঁকা স্থান রয়েছে কিনা সে বিষয়ে নিশ্চিত হন এবং সিস্টেম চালু করুন৷"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 31ecfc5..0778b0d 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -128,7 +128,7 @@
     <item msgid="4397097370387921767">"Wi-Fi pozivanje preko operatera %s"</item>
   </string-array>
     <string name="wifi_calling_off_summary" msgid="8720659586041656098">"Isključeno"</string>
-    <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Prednost ima Wi-Fi"</string>
+    <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferira se Wi-Fi"</string>
     <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferira se mobilna mreža"</string>
     <string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Samo Wi-Fi"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije proslijeđen"</string>
@@ -174,6 +174,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Radni profil je izbrisan jer nedostaje aplikacija administratora"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Nedostaje aplikacija administratora za radni profil ili je neispravna. Zbog toga su vaš radni profil i povezani podaci izbrisani. Obratite administratoru za pomoć."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Radni profil više nije dostupan na ovom uređaju"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Uređajem se upravlja."</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Vaša organizacija upravlja ovim uređajem i može pratiti mrežni saobraćaj. Dodirnite za detalje."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Uređaj će biti izbrisan"</string>
@@ -350,7 +352,7 @@
     <string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Dozvoljava aplikaciji da jednim dijelom trajno ostaje u memoriji. Time se ostalim aplikacijama dostupna memorija može ograničiti te usporiti rad TV-a."</string>
     <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"Omogućava aplikaciji da neke svoje dijelove pohrani trajno u memoriji. Ovo može ograničiti veličinu raspoložive memorije za druge aplikacije i tako usporiti telefon."</string>
     <string name="permlab_getPackageSize" msgid="7472921768357981986">"mjerenje prostora kojeg aplikacije zauzimaju u pohrani"</string>
-    <string name="permdesc_getPackageSize" msgid="3921068154420738296">"Dozvoljava aplikaciji preuzimanje svog kôda, podataka i veličine keš memorije"</string>
+    <string name="permdesc_getPackageSize" msgid="3921068154420738296">"Dozvoljava aplikaciji preuzimanje svog koda, podataka i veličine keš memorije"</string>
     <string name="permlab_writeSettings" msgid="2226195290955224730">"izmjena postavki sistema"</string>
     <string name="permdesc_writeSettings" msgid="7775723441558907181">"Dozvoljava aplikaciji izmijenu postavki sistema. Zlonamjerne aplikacije mogu oštetiti konfiguraciju sistema."</string>
     <string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"pokrenuti pri pokretanju"</string>
@@ -552,7 +554,7 @@
     <string name="permlab_access_notification_policy" msgid="4247510821662059671">"pristup opciji Ne ometaj"</string>
     <string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Omogućava aplikaciji da čita i upisuje konfiguraciju opcije Ne ometaj."</string>
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Postavljanje pravila za lozinke"</string>
-    <string name="policydesc_limitPassword" msgid="2502021457917874968">"Kontrolira dužinu i znakove koji su dozvoljeni u lozinkama za zaključavanje ekrana i PIN kodovima."</string>
+    <string name="policydesc_limitPassword" msgid="2502021457917874968">"Kontrolira dužinu i znakove koji su dozvoljeni u lozinkama za zaključavanje ekrana i PIN-ovima."</string>
     <string name="policylab_watchLogin" msgid="5091404125971980158">"Prati pokušaje otključavanja ekrana"</string>
     <string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Prati broj pogrešno unijetih lozinki prilikom otključavanja ekrana i zaključava tablet ili briše sve podatke s njega ukoliko se previše puta unese pogrešna lozinka."</string>
     <string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Prati koliko puta je lozinka neispravno otkucana prilikom otključavanja ekrana i zaključaj TV ili izbriši sve podatke s TV-a ako se lozinka neispravno ukuca previše puta."</string>
@@ -1000,10 +1002,8 @@
     <string name="dial" msgid="4204975095406423102">"Pozovi"</string>
     <string name="map" msgid="6068210738233985748">"Mape"</string>
     <string name="browse" msgid="6993590095938149861">"Preglednik"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke funkcije sistema možda neće raditi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nema dovoljno prostora za sistem. Obezbijedite 250MB slobodnog prostora i ponovo pokrenite uređaj."</string>
@@ -1489,14 +1489,14 @@
     <string name="kg_pin_instructions" msgid="2377242233495111557">"Unesite PIN"</string>
     <string name="kg_password_instructions" msgid="5753646556186936819">"Unesite lozinku"</string>
     <string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM je sada onemogućen. Unesite PUK kôd da nastavite. Za više informacija obratite se operateru."</string>
-    <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Unesite željeni PIN kôd"</string>
-    <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Potvrdi željeni PIN kôd"</string>
+    <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Unesite željeni PIN"</string>
+    <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Potvrdi željeni PIN"</string>
     <string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Otključavanje SIM kartice…"</string>
-    <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Pogrešan PIN kôd."</string>
+    <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Pogrešan PIN."</string>
     <string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Unesite PIN koji sadrži od 4 do 8 brojeva."</string>
     <string name="kg_invalid_sim_puk_hint" msgid="6025069204539532000">"PUK kôd bi trebao imati 8 brojeva."</string>
     <string name="kg_invalid_puk" msgid="3638289409676051243">"Ponovo unesite ispravan PUK kôd. Ponovljeni pokušaji će trajno onemogućiti SIM."</string>
-    <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN kodovi se ne poklapaju"</string>
+    <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN-ovi se ne poklapaju"</string>
     <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Previše pokušaja otključavanja pomoću uzorka"</string>
     <string name="kg_login_instructions" msgid="1100551261265506448">"Da otključate, prijavite se sa svojim Google računom."</string>
     <string name="kg_login_username_hint" msgid="5718534272070920364">"Korisničko ime (adresa e-pošte)"</string>
@@ -1626,7 +1626,7 @@
     <string name="reason_service_unavailable" msgid="7824008732243903268">"Usluga štampanja nije omogućena."</string>
     <string name="print_service_installed_title" msgid="2246317169444081628">"Usluga <xliff:g id="NAME">%s</xliff:g> je instalirana"</string>
     <string name="print_service_installed_message" msgid="5897362931070459152">"Dodirnite da omogućite"</string>
-    <string name="restr_pin_enter_admin_pin" msgid="8641662909467236832">"Upišite PIN kôd administratora"</string>
+    <string name="restr_pin_enter_admin_pin" msgid="8641662909467236832">"Upišite PIN administratora"</string>
     <string name="restr_pin_enter_pin" msgid="3395953421368476103">"Unesite PIN"</string>
     <string name="restr_pin_incorrect" msgid="8571512003955077924">"Netačno"</string>
     <string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"Trenutni PIN"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 082513d..d910bf73 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -127,7 +127,7 @@
     <item msgid="4397097370387921767">"Trucada de Wi-Fi de: %s"</item>
   </string-array>
     <string name="wifi_calling_off_summary" msgid="8720659586041656098">"Desactivat"</string>
-    <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferència per la Wi-Fi"</string>
+    <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferència per a la Wi-Fi"</string>
     <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferència per a dades mòbils"</string>
     <string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Només Wi-Fi"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no s\'ha desviat"</string>
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"S\'ha suprimit el perfil professional perquè falta l\'aplicació d\'administració"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Falta l\'aplicació d\'administració del perfil professional o està malmesa. Com a conseqüència, s\'han suprimit el teu perfil professional i les dades relacionades. Contacta amb l\'administrador per obtenir ajuda."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"El teu perfil professional ja no està disponible en aquest dispositiu"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"El dispositiu està gestionat"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"La teva organització gestiona aquest dispositiu i és possible que supervisi el trànsit de xarxa. Toca per obtenir més informació."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"El contingut del dispositiu s\'esborrarà"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Truca"</string>
     <string name="map" msgid="6068210738233985748">"Mapes"</string>
     <string name="browse" msgid="6993590095938149861">"Navegador"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contacte"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"L\'espai d\'emmagatzematge s\'està esgotant"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"És possible que algunes funcions del sistema no funcionin"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"No hi ha prou espai d\'emmagatzematge per al sistema. Comprova que tinguis 250 MB d\'espai lliure i reinicia."</string>
@@ -1086,13 +1086,13 @@
     <string name="volume_call" msgid="3941680041282788711">"Volum en trucada"</string>
     <string name="volume_bluetooth_call" msgid="2002891926351151534">"Volum en trucada Bluetooth"</string>
     <string name="volume_alarm" msgid="1985191616042689100">"Volum de l\'alarma"</string>
-    <string name="volume_notification" msgid="2422265656744276715">"Volum de notificació"</string>
+    <string name="volume_notification" msgid="2422265656744276715">"Volum de notificacions"</string>
     <string name="volume_unknown" msgid="1400219669770445902">"Volum"</string>
     <string name="volume_icon_description_bluetooth" msgid="6538894177255964340">"Volum de Bluetooth"</string>
     <string name="volume_icon_description_ringer" msgid="3326003847006162496">"Volum del so"</string>
     <string name="volume_icon_description_incall" msgid="8890073218154543397">"Volum de trucada"</string>
     <string name="volume_icon_description_media" msgid="4217311719665194215">"Volum de multimèdia"</string>
-    <string name="volume_icon_description_notification" msgid="7044986546477282274">"Volum de notificació"</string>
+    <string name="volume_icon_description_notification" msgid="7044986546477282274">"Volum de notificacions"</string>
     <string name="ringtone_default" msgid="3789758980357696936">"So predeterminat"</string>
     <string name="ringtone_default_with_actual" msgid="1767304850491060581">"Predeterminat (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
     <string name="ringtone_silent" msgid="7937634392408977062">"Cap"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 23f3cb9..c0760b1 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -176,6 +176,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Pracovní profil byl smazán, protože není k dispozici aplikace pro správu"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplikace pro správu pracovního profilu chybí nebo je poškozena. Váš pracovní profil a související data proto byla smazána. Požádejte o pomoc administrátora."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Váš pracovní profil v tomto zařízení již není k dispozici"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Zařízení je spravováno"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Toto zařízení je spravováno vaší organizací, která může sledovat síťový provoz. Podrobnosti zobrazíte klepnutím."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Zařízení bude vymazáno"</string>
@@ -1020,10 +1022,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefon"</string>
     <string name="map" msgid="6068210738233985748">"Mapy"</string>
     <string name="browse" msgid="6993590095938149861">"Prohlížeč"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"V úložišti je málo místa"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Některé systémové funkce nemusí fungovat"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Pro systém není dostatek místa v úložišti. Uvolněte alespoň 250 MB místa a restartujte zařízení."</string>
@@ -1112,7 +1112,7 @@
     <string name="heavy_weight_switcher_text" msgid="7022631924534406403">"Než spustíte novou aplikaci, je třeba zastavit jinou spuštěnou aplikaci."</string>
     <string name="old_app_action" msgid="493129172238566282">"Návrat do aplikace <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
     <string name="old_app_description" msgid="2082094275580358049">"Nespouštět novou aplikaci."</string>
-    <string name="new_app_action" msgid="5472756926945440706">"Spustit aplikaci <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
+    <string name="new_app_action" msgid="5472756926945440706">"Do aplikace <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
     <string name="new_app_description" msgid="1932143598371537340">"Zastavit starou aplikaci bez uložení."</string>
     <string name="dump_heap_notification" msgid="2618183274836056542">"Proces <xliff:g id="PROC">%1$s</xliff:g> překročil limit paměti"</string>
     <string name="dump_heap_notification_detail" msgid="6901391084243999274">"Byl shromážděn výpis haldy, klepnutím jej můžete sdílet"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 9360cbe..7b941b3 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Arbejdsprofilen blev slettet, fordi der mangler en administrationsapp"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Administrationsappen til arbejdsprofilen mangler eller er beskadiget. Derfor er din arbejdsprofil og dine relaterede data blevet slettet. Kontakt din administrator for at få hjælp."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Din arbejdsprofil er ikke længere tilgængelig på denne enhed"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Dette er en administreret enhed"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Din organisation administrerer denne enhed og kan overvåge netværkstrafik. Tryk for at se oplysninger."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Enheden slettes"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Opkald"</string>
     <string name="map" msgid="6068210738233985748">"Kort"</string>
     <string name="browse" msgid="6993590095938149861">"Browser"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"Sms"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontaktperson"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der er snart ikke mere lagerplads"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nogle systemfunktioner virker måske ikke"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Der er ikke nok ledig lagerplads til systemet. Sørg for, at du har 250 MB ledig plads, og genstart."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 8b31d96..68b0355 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Arbeitsprofil aufgrund fehlender Admin-App gelöscht"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Die Admin-App für das Arbeitsprofil fehlt oder ist beschädigt. Daher wurden dein Arbeitsprofil und alle zugehörigen Daten gelöscht. Bitte wende dich für weitere Hilfe an deinen Administrator."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Dein Arbeitsprofil ist auf diesem Gerät nicht mehr verfügbar"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Dies ist ein verwaltetes Gerät"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Deine Organisation verwaltet dieses Gerät und überprüft unter Umständen den Netzwerkverkehr. Tippe hier, um weitere Informationen zu erhalten."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Die Daten auf deinem Gerät werden gelöscht."</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefon"</string>
     <string name="map" msgid="6068210738233985748">"Karten"</string>
     <string name="browse" msgid="6993590095938149861">"Browser"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der Speicherplatz wird knapp"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Einige Systemfunktionen funktionieren möglicherweise nicht."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Der Speicherplatz reicht nicht für das System aus. Stelle sicher, dass 250 MB freier Speicherplatz vorhanden sind, und starte das Gerät dann neu."</string>
@@ -993,8 +993,7 @@
     <string name="cancel" msgid="6442560571259935130">"Abbrechen"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
     <string name="no" msgid="5141531044935541497">"Abbrechen"</string>
-    <!-- no translation found for close (2318214661230355730) -->
-    <skip />
+    <string name="close" msgid="2318214661230355730">"SCHLIEẞEN"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Achtung"</string>
     <string name="loading" msgid="7933681260296021180">"Wird geladen…"</string>
     <string name="capital_on" msgid="1544682755514494298">"AN"</string>
@@ -1051,10 +1050,8 @@
     <string name="screen_compat_mode_scale" msgid="3202955667675944499">"Skalieren"</string>
     <string name="screen_compat_mode_show" msgid="4013878876486655892">"Immer anzeigen"</string>
     <string name="screen_compat_mode_hint" msgid="1064524084543304459">"Eine erneute Aktivierung ist in den Systemeinstellungen unter \"Apps &gt; Heruntergeladen\" möglich."</string>
-    <!-- no translation found for top_app_killed_title (6814231368167994497) -->
-    <skip />
-    <!-- no translation found for top_app_killed_message (3487519022191609844) -->
-    <skip />
+    <string name="top_app_killed_title" msgid="6814231368167994497">"App reagiert nicht"</string>
+    <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> belegt möglicherweise zu viel Speicherplatz."</string>
     <string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> unterstützt nicht die aktuelle Einstellung für die Anzeigegröße, sodass ein unerwartetes Verhalten auftreten kann."</string>
     <string name="unsupported_display_size_show" msgid="7969129195360353041">"Immer anzeigen"</string>
     <string name="smv_application" msgid="3307209192155442829">"Die App <xliff:g id="APPLICATION">%1$s</xliff:g> (Prozess <xliff:g id="PROCESS">%2$s</xliff:g>) hat gegen deine selbsterzwungene StrictMode-Richtlinie verstoßen."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 19aafc8..9ca0a86 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Το προφίλ εργασίας διαγράφηκε λόγω απουσίας της εφαρμογής διαχείρισης"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Η εφαρμογή διαχείρισης προφίλ εργασίας είτε λείπει είτε είναι κατεστραμμένη. Ως αποτέλεσμα, διαγράφηκε το προφίλ εργασίας και τα σχετικά δεδομένα. Επικοινωνήστε με τον διαχειριστή σας για βοήθεια."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Το προφίλ εργασίας σας δεν είναι πια διαθέσιμο σε αυτήν τη συσκευή"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Η συσκευή είναι διαχειριζόμενη"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Ο οργανισμός σας διαχειρίζεται αυτήν τη συσκευή και ενδέχεται να παρακολουθεί την επισκεψιμότητα δικτύου. Πατήστε για λεπτομέρειες."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Η συσκευή σας θα διαγραφεί"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Τηλέφωνο"</string>
     <string name="map" msgid="6068210738233985748">"Χάρτες"</string>
     <string name="browse" msgid="6993590095938149861">"Πρόγραμμα περιήγησης"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Επαφή"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ο αποθηκευτικός χώρος εξαντλείται"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Ορισμένες λειτουργίες συστήματος ενδέχεται να μην λειτουργούν"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Δεν υπάρχει αρκετός αποθηκευτικός χώρος για το σύστημα. Βεβαιωθείτε ότι διαθέτετε 250 MB ελεύθερου χώρου και κάντε επανεκκίνηση."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 7fff878..73e55da 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Work profile deleted due to missing admin app"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"The work profile admin app is either missing or corrupted. As a result, your work profile and related data have been deleted. Contact your admin for assistance."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Your work profile is no longer available on this device"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Too many password attempts"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Device is managed"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Your organisation manages this device and may monitor network traffic. Tap for details."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Your device will be erased"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Phone"</string>
     <string name="map" msgid="6068210738233985748">"Maps"</string>
     <string name="browse" msgid="6993590095938149861">"Browser"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 7fff878..73e55da 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Work profile deleted due to missing admin app"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"The work profile admin app is either missing or corrupted. As a result, your work profile and related data have been deleted. Contact your admin for assistance."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Your work profile is no longer available on this device"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Too many password attempts"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Device is managed"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Your organisation manages this device and may monitor network traffic. Tap for details."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Your device will be erased"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Phone"</string>
     <string name="map" msgid="6068210738233985748">"Maps"</string>
     <string name="browse" msgid="6993590095938149861">"Browser"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 7fff878..73e55da 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Work profile deleted due to missing admin app"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"The work profile admin app is either missing or corrupted. As a result, your work profile and related data have been deleted. Contact your admin for assistance."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Your work profile is no longer available on this device"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Too many password attempts"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Device is managed"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Your organisation manages this device and may monitor network traffic. Tap for details."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Your device will be erased"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Phone"</string>
     <string name="map" msgid="6068210738233985748">"Maps"</string>
     <string name="browse" msgid="6993590095938149861">"Browser"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 7fff878..73e55da 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Work profile deleted due to missing admin app"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"The work profile admin app is either missing or corrupted. As a result, your work profile and related data have been deleted. Contact your admin for assistance."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Your work profile is no longer available on this device"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Too many password attempts"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Device is managed"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Your organisation manages this device and may monitor network traffic. Tap for details."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Your device will be erased"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Phone"</string>
     <string name="map" msgid="6068210738233985748">"Maps"</string>
     <string name="browse" msgid="6993590095938149861">"Browser"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index b93db1c..db7ab4f 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‏‏‎‎‏‏‎‎‏‎‏‏‎‏‏‎‎‏‎‏‏‏‎‎‎‎Work profile deleted due to missing admin app‎‏‎‎‏‎"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‎‏‏‎‏‎‎‏‎‏‎‎‏‎‏‏‎‏‎‏‏‎‎‎‎‎‎‎‎‎‏‎‎‎‏‏‏‎‎‎‎‎‎‏‎‎‎‎The work profile admin app is either missing or corrupted. As a result, your work profile and related data have been deleted. Contact your admin for assistance.‎‏‎‎‏‎"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‎Your work profile is no longer available on this device‎‏‎‎‏‎"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‎‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‎‎‎‏‎‎‏‏‏‎‎Too many password attempts‎‏‎‎‏‎"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‎‏‎‎‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‏‎Device is managed‎‏‎‎‏‎"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‎‏‎‏‎‏‎‏‏‎‎‎‏‎‏‎‎‎‎‏‎‎‏‏‎‎‎‏‏‎‎‎‎‎‎‎‎‎‏‎‎‎‎‎‏‎‏‎‏‎‎Your organization manages this device and may monitor network traffic. Tap for details.‎‏‎‎‏‎"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‎‎‎‏‏‎‏‏‎‎‎‏‎‎‏‏‎Your device will be erased‎‏‎‎‏‎"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‏‎‏‏‎‎‎‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎‎‎‎‎‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‎‎‎‎‏‏‏‏‏‎‎Phone‎‏‎‎‏‎"</string>
     <string name="map" msgid="6068210738233985748">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‎‏‎‎‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‎‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‏‎‏‎‏‎‎‎Maps‎‏‎‎‏‎"</string>
     <string name="browse" msgid="6993590095938149861">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‎‏‏‏‎‎‎‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‏‎‏‏‏‎‏‏‏‏‎‎‏‎‏‎Browser‎‏‎‎‏‎"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‏‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‎SMS‎‏‎‎‏‎"</string>
+    <string name="add_contact" msgid="7990645816259405444">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‏‎‏‎‎‏‏‎‎‏‏‎‏‎‎‎‎‏‎‎‎Contact‎‏‎‎‏‎"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‏‎‎‏‏‎‎‏‎‎‏‎‏‎‏‎‎‎‎‏‎‎‏‎‏‎‏‎‎‎‎‏‎‎‏‎‎‎‎‏‏‎‎‎‏‎‎Storage space running out‎‏‎‎‏‎"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‏‎‎‎‎‏‏‎‏‏‎‏‏‏‎‏‏‎‏‎‎‎‎‎‎‎‎‎‎‎‎‏‏‏‎‎‎‏‏‏‎‎‎‏‏‎Some system functions may not work‎‏‎‎‏‎"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‏‏‏‎‏‎‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‏‏‎‎‏‏‎‏‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‎Not enough storage for the system. Make sure you have 250MB of free space and restart.‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 3946e7d..16a43ba 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Se borró el perfil de trabajo debido a la falta de una app de administración"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"La app de administración de perfil de trabajo no se encuentra o está dañada. Por lo tanto, se borraron tu perfil de trabajo y los datos relacionados. Para obtener asistencia, comunícate con el administrador."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Tu perfil de trabajo ya no está disponible en este dispositivo"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Demasiados intentos para ingresar la contraseña"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Dispositivo administrado"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Tu organización administra este dispositivo y es posible que controle el tráfico de red. Presiona para obtener más información."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Se borrarán los datos del dispositivo"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Teléfono"</string>
     <string name="map" msgid="6068210738233985748">"Mapas"</string>
     <string name="browse" msgid="6993590095938149861">"Navegador"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contacto"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio de almacenamiento"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no estén disponibles."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"No hay espacio suficiente para el sistema. Asegúrate de que haya 250 MB libres y reinicia el dispositivo."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 7085fda..5827a97 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -128,7 +128,7 @@
   </string-array>
     <string name="wifi_calling_off_summary" msgid="8720659586041656098">"Desactivado"</string>
     <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferir Wi-Fi"</string>
-    <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferencia a datos móviles"</string>
+    <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferir datos móviles"</string>
     <string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Solo conexión Wi-Fi"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: No desviada"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Se ha eliminado el perfil de trabajo porque falta la aplicación de administración"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Falta la aplicación de administración del perfil de trabajo o está dañada. Por ello, se han eliminado tu perfil de trabajo y los datos relacionados. Ponte en contacto con el administrador para obtener ayuda."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Tu perfil de trabajo ya no está disponible en este dispositivo"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Has fallado demasiadas veces al introducir la contraseña"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"El dispositivo está administrado"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Tu organización administra este dispositivo y puede supervisar el tráfico de red. Toca la notificación para obtener más información."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Tu dispositivo se borrará"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Teléfono"</string>
     <string name="map" msgid="6068210738233985748">"Mapas"</string>
     <string name="browse" msgid="6993590095938149861">"Navegador"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contacto"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no funcionen."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"No hay espacio suficiente para el sistema. Comprueba que haya 250 MB libres y reinicia el dispositivo."</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 2565e62..1d40f4a 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Tööprofiil on kustutatud puuduva administraatori rakenduse tõttu"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Tööprofiili administraatori rakendus puudub või on rikutud. Seetõttu on teie tööprofiil ja seotud andmed kustutatud. Abi saamiseks võtke ühendust administraatoriga."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Teie tööprofiil pole selles seadmes enam saadaval"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Liiga palju paroolikatseid"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Seade on hallatud"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Teie organisatsioon haldab seda seadet ja võib jälgida võrguliiklust. Puudutage üksikasjade vaatamiseks."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Seade kustutatakse"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefon"</string>
     <string name="map" msgid="6068210738233985748">"Maps"</string>
     <string name="browse" msgid="6993590095938149861">"Brauser"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Talletusruum saab täis"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Mõned süsteemifunktsioonid ei pruugi töötada"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Süsteemis pole piisavalt talletusruumi. Veenduge, et seadmes oleks 250 MB vaba ruumi, ja käivitage seade uuesti."</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index f88d4c4..a864f94 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Laneko profila ezabatu egin da hura administratzeko aplikazioa falta delako"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Laneko profila administratzeko aplikazioa falta da edo hondatuta dago. Ondorioz, ezabatu egin dira laneko profila bera eta harekin erlazionatutako datuak. Laguntza lortzeko, jarri administratzailearekin harremanetan."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Laneko profila ez dago erabilgarri gailu honetan"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Gehiegitan saiatu zara pasahitza idazten"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Jabeak kudeatzen du gailua"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Erakundeak kudeatzen du gailua eta baliteke sareko trafikoa gainbegiratzea. Sakatu hau xehetasunak ikusteko."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Gailuko datuak ezabatu egingo dira"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefonoa"</string>
     <string name="map" msgid="6068210738233985748">"Mapak"</string>
     <string name="browse" msgid="6993590095938149861">"Arakatzailea"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMSa"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontaktua"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Memoria betetzen ari da"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sistemaren funtzio batzuek ez dute agian funtzionatuko"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sisteman ez dago behar adina memoria. Ziurtatu gutxienez 250 MB erabilgarri dituzula eta, ondoren, berrabiarazi gailua."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 2bd7696..77336ff 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"به دلیل نداشتن برنامه سرپرست، نمایه کاری حذف شد"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"برنامه سرپرست نمایه کاری یا وجود ندارد یا خراب است. در نتیجه، نمایه کاری شما و داده‌های مرتبط با آن حذف شده است. برای دریافت راهنمایی با سرپرست سیستم تماس بگیرید."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"نمایه کاری شما دیگر در این دستگاه دردسترس نیست"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"دستگاه مدیریت می‌شود"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"سازمانتان این دستگاه را مدیریت می‌کند و ممکن است ترافیک شبکه را پایش کند. برای اطلاع از جزئیات، ضربه بزنید."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"دستگاهتان پاک خواهد شد"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"تلفن"</string>
     <string name="map" msgid="6068210738233985748">"نقشه‌ها"</string>
     <string name="browse" msgid="6993590095938149861">"مرورگر"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"پیامک"</string>
+    <string name="add_contact" msgid="7990645816259405444">"مخاطب"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"حافظه درحال پر شدن است"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"برخی از عملکردهای سیستم ممکن است کار نکنند"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"فضای ذخیره‌سازی سیستم کافی نیست. اطمینان حاصل کنید که دارای ۲۵۰ مگابایت فضای خالی هستید و سیستم را راه‌اندازی مجدد کنید."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index c2a2ccb63..0c4e883 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Työprofiili poistettiin, koska laitteesta puuttuu hallintasovellus."</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Työprofiilin hallintasovellus puuttuu tai se on vioittunut. Tästä syystä työprofiilisi ja siihen liittyvät tiedot on poistettu. Pyydä ohjeita järjestelmänvalvojaltasi."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Työprofiilisi ei ole enää käytettävissä tällä laitteella."</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Liikaa salasanayrityksiä"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Hallinnoitu laite"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Organisaatiosi hallinnoi tätä laitetta ja voi tarkkailla verkkoliikennettä. Katso lisätietoja napauttamalla."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Laitteen tiedot poistetaan"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Puhelin"</string>
     <string name="map" msgid="6068210738233985748">"Kartat"</string>
     <string name="browse" msgid="6993590095938149861">"Selain"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"Tekstiviesti"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Yhteystieto"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Tallennustila loppumassa"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Kaikki järjestelmätoiminnot eivät välttämättä toimi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Tallennustila ei riitä. Varmista, että vapaata tilaa on 250 Mt, ja käynnistä uudelleen."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index fbed666..a8ab142 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil professionnel supprimé en raison de l\'application d\'administration manquante"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Le profil professionnel de l\'application d\'administration est manquant ou corrompu. Votre profil professionnel et ses données connexes ont donc été supprimés. Communiquez avec votre administrateur pour obtenir de l\'assistance."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Votre profil professionnel n\'est plus accessible sur cet appareil"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"L\'appareil est géré"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Votre organisation gère cet appareil et peut surveiller le trafic réseau. Touchez ici pour obtenir plus d\'information."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Le contenu de votre appareil sera effacé"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Téléphone"</string>
     <string name="map" msgid="6068210738233985748">"Cartes"</string>
     <string name="browse" msgid="6993590095938149861">"Navigateur"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"Messagerie texte"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Espace de stockage bientôt saturé"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index fcdccad..4c2bcca 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil professionnel supprimé, car une application d\'administration est manquante"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"L\'application d\'administration du profil professionnel est manquante ou endommagée. Par conséquent, votre profil professionnel et toutes les données associées ont été supprimés. Pour obtenir de l\'aide, contactez l\'administrateur."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Votre profil professionnel n\'est plus disponible sur cet appareil"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Trop de tentatives de saisie du mot de passe"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"L\'appareil est géré"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Votre organisation gère cet appareil et peut surveiller le trafic réseau. Appuyez ici pour obtenir plus d\'informations."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Les données de votre appareil vont être effacées"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Téléphone"</string>
     <string name="map" msgid="6068210738233985748">"Cartes"</string>
     <string name="browse" msgid="6993590095938149861">"Navigateur"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Espace de stockage bientôt saturé"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 0c0e12a..12cbbb3 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -52,8 +52,8 @@
     </plurals>
     <string name="imei" msgid="2625429890869005782">"IMEI"</string>
     <string name="meid" msgid="4841221237681254195">"MEID"</string>
-    <string name="ClipMmi" msgid="6952821216480289285">"ID de chamada entrante"</string>
-    <string name="ClirMmi" msgid="7784673673446833091">"ID de chamada saínte"</string>
+    <string name="ClipMmi" msgid="6952821216480289285">"Identificador de chamada entrante"</string>
+    <string name="ClirMmi" msgid="7784673673446833091">"Identificador de chamada saínte"</string>
     <string name="ColpMmi" msgid="3065121483740183974">"ID de liña conectada"</string>
     <string name="ColrMmi" msgid="4996540314421889589">"Restrición de ID de liña conectada"</string>
     <string name="CfMmi" msgid="5123218989141573515">"Desvío de chamadas"</string>
@@ -67,24 +67,24 @@
     <string name="RuacMmi" msgid="7827887459138308886">"Rexeitamento de chamadas molestas non desexadas"</string>
     <string name="CndMmi" msgid="3116446237081575808">"Entrega de número de chamada entrante"</string>
     <string name="DndMmi" msgid="1265478932418334331">"Non molestar"</string>
-    <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"De forma predeterminada, restrínxese o ID de chamada. Próxima chamada: restrinxido."</string>
-    <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"De forma predeterminada, restrínxese o ID de chamada. Próxima chamada: non restrinxido."</string>
-    <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"De forma predeterminada, non se restrinxe o ID de chamada. Próxima chamada: restrinxido."</string>
-    <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"De forma predeterminada, non se restrinxe o ID de chamada. Próxima chamada: non restrinxido."</string>
+    <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"O valor predeterminado do identificador de chamada é restrinxido. Próxima chamada: restrinxido"</string>
+    <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"O valor predeterminado do identificador de chamada é restrinxido. Próxima chamada: non restrinxido"</string>
+    <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"O valor predeterminado do identificador de chamada é non restrinxido. Próxima chamada: restrinxido"</string>
+    <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"O valor predeterminado do identificador de chamada é restrinxido. Próxima chamada: non restrinxido"</string>
     <string name="serviceNotProvisioned" msgid="8614830180508686666">"Servizo non ofrecido."</string>
-    <string name="CLIRPermanent" msgid="3377371145926835671">"Non podes cambiar a configuración do ID de chamada."</string>
+    <string name="CLIRPermanent" msgid="3377371145926835671">"Non podes cambiar a configuración do identificador de chamada."</string>
     <string name="RestrictedOnDataTitle" msgid="1322504692764166532">"Non hai servizo de datos"</string>
-    <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"Non se poden realizar chamadas de emerxencia"</string>
+    <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"Non se poden realizar chamadas de urxencia"</string>
     <string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"Non hai servizo de chamadas de voz"</string>
-    <string name="RestrictedOnAllVoiceTitle" msgid="158800171499150681">"Non hai servizo de chamadas de emerxencia nin de voz"</string>
+    <string name="RestrictedOnAllVoiceTitle" msgid="158800171499150681">"Non hai servizo de chamadas de urxencia nin de voz"</string>
     <string name="RestrictedStateContent" msgid="4278821484643362350">"A rede de telefonía móbil non ofrece o servizo na túa localización temporalmente"</string>
     <string name="NetworkPreferenceSwitchTitle" msgid="4008877505368566980">"Non se pode conectar coa rede"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="1203771446683319957">"Para mellorar a recepción, proba a cambiar o tipo seleccionado en Configuración &gt; Rede e Internet &gt; Redes de telefonía móbil &gt; Tipo de rede preferido."</string>
     <string name="EmergencyCallWarningTitle" msgid="4790413876281901612">"As chamadas por wifi están activadas"</string>
-    <string name="EmergencyCallWarningSummary" msgid="8973232888021643293">"As chamadas de emerxencia precisan unha rede de telefonía móbil."</string>
+    <string name="EmergencyCallWarningSummary" msgid="8973232888021643293">"As chamadas de urxencia precisan unha rede de telefonía móbil."</string>
     <string name="notification_channel_network_alert" msgid="4427736684338074967">"Alertas"</string>
     <string name="notification_channel_call_forward" msgid="2419697808481833249">"Desvío de chamadas"</string>
-    <string name="notification_channel_emergency_callback" msgid="6686166232265733921">"Modo de devolución de chamadas de emerxencia"</string>
+    <string name="notification_channel_emergency_callback" msgid="6686166232265733921">"Modo de devolución de chamadas de urxencia"</string>
     <string name="notification_channel_mobile_data_status" msgid="4575131690860945836">"Estado dos datos móbiles"</string>
     <string name="notification_channel_sms" msgid="3441746047346135073">"Mensaxes SMS"</string>
     <string name="notification_channel_voice_mail" msgid="3954099424160511919">"Mensaxes de correo de voz"</string>
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Eliminouse o perfil de traballo porque falta a aplicación de administración"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Falta a aplicación de administración do perfil de traballo ou ben está danada. Como resultado, eliminouse o teu perfil de traballo e os datos relacionados. Para obter asistencia, contacta co administrador."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"O teu perfil de traballo xa non está dispoñible neste dispositivo"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"O dispositivo está xestionado"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"A túa organización xestiona este dispositivo e pode controlar o tráfico de rede. Toca para obter máis detalles."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Borrarase o teu dispositivo"</string>
@@ -188,7 +190,7 @@
     <string name="silent_mode_silent" msgid="319298163018473078">"Timbre desactivado"</string>
     <string name="silent_mode_vibrate" msgid="7072043388581551395">"Timbre en vibración"</string>
     <string name="silent_mode_ring" msgid="8592241816194074353">"Timbre activado"</string>
-    <string name="reboot_to_update_title" msgid="6212636802536823850">"Actualización do sistema de Android"</string>
+    <string name="reboot_to_update_title" msgid="6212636802536823850">"Actualización do sistema Android"</string>
     <string name="reboot_to_update_prepare" msgid="6305853831955310890">"Preparando para actualizar…"</string>
     <string name="reboot_to_update_package" msgid="3871302324500927291">"Procesando paquete de actualización…"</string>
     <string name="reboot_to_update_reboot" msgid="6428441000951565185">"Reiniciando..."</string>
@@ -209,7 +211,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opcións de teléfono"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloqueo de pantalla"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Apagar"</string>
-    <string name="global_action_emergency" msgid="7112311161137421166">"Emerxencias"</string>
+    <string name="global_action_emergency" msgid="7112311161137421166">"Urxencias"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Informe de erros"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Crear informe de erros"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Este informe recompilará información acerca do estado actual do teu dispositivo para enviala en forma de mensaxe de correo electrónico. O informe de erros tardará un pouco en completarse desde o seu inicio ata que estea preparado para enviarse, polo que che recomendamos que teñas paciencia."</string>
@@ -315,7 +317,7 @@
     <string name="permlab_receiveMms" msgid="1821317344668257098">"recibir mensaxes de texto (MMS)"</string>
     <string name="permdesc_receiveMms" msgid="533019437263212260">"Permite á aplicación recibir e procesar mensaxes MMS. Isto significa que a aplicación pode supervisar ou eliminar mensaxes enviadas ao teu dispositivo sen mostrarchas."</string>
     <string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"ler mensaxes de difusión móbil"</string>
-    <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite á aplicación ler mensaxes de difusión móbil recibidas polo teu dispositivo. As alertas de difusión móbil envíanse nalgunhas localizacións para avisar de situacións de emerxencia. É posible que aplicacións maliciosas afecten ao rendemento ou funcionamento do teu dispositivo cando se recibe unha difusión móbil de emerxencia."</string>
+    <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite á aplicación ler mensaxes de difusión móbil recibidas polo teu dispositivo. As alertas de difusión móbil envíanse nalgunhas localizacións para avisar de situacións de urxencia. É posible que aplicacións maliciosas afecten ao rendemento ou funcionamento do teu dispositivo cando se recibe unha difusión móbil de urxencia."</string>
     <string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ler feeds subscritos"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Permite á aplicación obter detalles acerca dos feeds sincronizados actualmente."</string>
     <string name="permlab_sendSms" msgid="7544599214260982981">"enviar e consultar mensaxes de SMS"</string>
@@ -401,7 +403,7 @@
     <string name="permlab_vibrate" msgid="7696427026057705834">"controlar a vibración"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Permite á aplicación controlar o vibrador."</string>
     <string name="permlab_callPhone" msgid="3925836347681847954">"chamar directamente aos números de teléfono"</string>
-    <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite á aplicación chamar a números de teléfono sen a túa intervención. Esta acción pode implicar chamadas ou custos inesperados. Ten en conta que isto non permite á aplicación chamar a números de emerxencia. É posible que aplicacións maliciosas che custen diñeiro debido á realización de chamadas sen a túa confirmación."</string>
+    <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite á aplicación chamar a números de teléfono sen a túa intervención. Esta acción pode implicar chamadas ou custos inesperados. Ten en conta que isto non permite á aplicación chamar a números de urxencia. É posible que aplicacións maliciosas che custen diñeiro debido á realización de chamadas sen a túa confirmación."</string>
     <string name="permlab_accessImsCallService" msgid="3574943847181793918">"acceso ao servizo de chamadas de IMS"</string>
     <string name="permdesc_accessImsCallService" msgid="8992884015198298775">"Permite que a aplicación use o servizo de IMS para facer chamadas sen a túa intervención."</string>
     <string name="permlab_readPhoneState" msgid="9178228524507610486">"ler o estado e a identidade do teléfono"</string>
@@ -702,13 +704,13 @@
     <string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Escribe o PIN para desbloquear"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorrecto"</string>
     <string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, preme Menú e, a continuación, 0."</string>
-    <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emerxencia"</string>
+    <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de urxencia"</string>
     <string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sen servizo"</string>
     <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantalla bloqueada"</string>
-    <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Preme Menú para desbloquear ou realizar unha chamada de emerxencia."</string>
+    <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Preme Menú para desbloquear ou realizar unha chamada de urxencia."</string>
     <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Preme Menú para desbloquear."</string>
     <string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Crea o padrón de desbloqueo"</string>
-    <string name="lockscreen_emergency_call" msgid="5298642613417801888">"Emerxencia"</string>
+    <string name="lockscreen_emergency_call" msgid="5298642613417801888">"Urxencia"</string>
     <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Volver á chamada"</string>
     <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcto!"</string>
     <string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Téntao de novo"</string>
@@ -730,7 +732,7 @@
     <string name="lockscreen_transport_stop_description" msgid="5907083260651210034">"Deter"</string>
     <string name="lockscreen_transport_rew_description" msgid="6944412838651990410">"Rebobinar"</string>
     <string name="lockscreen_transport_ffw_description" msgid="42987149870928985">"Avance rápido"</string>
-    <string name="emergency_calls_only" msgid="6733978304386365407">"Só chamadas de emerxencia"</string>
+    <string name="emergency_calls_only" msgid="6733978304386365407">"Só chamadas de urxencia"</string>
     <string name="lockscreen_network_locked_message" msgid="143389224986028501">"Bloqueada pola rede"</string>
     <string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"A tarxeta SIM está bloqueada con código PUK."</string>
     <string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Consulta a guía do usuario ou ponte en contacto co servizo de asistencia ao cliente."</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Teléfono"</string>
     <string name="map" msgid="6068210738233985748">"Mapas"</string>
     <string name="browse" msgid="6993590095938149861">"Navegador"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contacto"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Estase esgotando o espazo de almacenamento"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"É posible que algunhas funcións do sistema non funcionen"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Non hai almacenamento suficiente para o sistema. Asegúrate de ter un espazo libre de 250 MB e reinicia o dispositivo."</string>
@@ -1782,7 +1782,7 @@
     <string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Mantén a calma e busca refuxio cerca."</string>
     <string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"Abandona de inmediato rexións costeiras e situadas na beira de ríos para dirixirte a un lugar máis seguro, como un terreo elevado."</string>
     <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"Mantén a calma e busca refuxio cerca."</string>
-    <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Proba de mensaxes de emerxencia"</string>
+    <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Proba de mensaxes de urxencia"</string>
     <string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Responder"</string>
     <string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
     <string name="mmcc_authentication_reject" msgid="7729819349669603406">"Non se admite a tarxeta SIM"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index d985e28..4cb735d 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"ખૂટતી વ્યવસ્થાપક ઍપ્લિકેશનને કારણે કાર્યાલયની પ્રોફાઇલ કાઢી નાખી"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"કાર્ય પ્રોફાઇલ વ્યવસ્થાપક ઍપ્લિકેશન ખૂટે છે અથવા તો દૂષિત છે. પરિણામે, તમારી કાર્યાલયની પ્રોફાઇલ અને તે સંબંધિત ડેટા કાઢી નાખવામાં આવ્યો છે. સહાયતા માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"આ ઉપકરણ પર તમારી કાર્યાલયની પ્રોફાઇલ હવે ઉપલબ્ધ નથી"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"ઉપકરણ સંચાલિત છે"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"તમારી સંસ્થા આ ઉપકરણનું સંચાલન કરે છે અને નેટવર્ક ટ્રાફિફનું નિયમન કરી શકે છે. વિગતો માટે ટૅપ કરો."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"તમારું ઉપકરણ કાઢી નાખવામાં આવશે"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"ફોન"</string>
     <string name="map" msgid="6068210738233985748">"નકશા"</string>
     <string name="browse" msgid="6993590095938149861">"બ્રાઉઝર"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"સંપર્ક"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"સ્ટોરેજ સ્થાન સમાપ્ત થયું"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"કેટલાક સિસ્ટમ કાર્યો કામ કરી શકશે નહીં"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"સિસ્ટમ માટે પર્યાપ્ત સ્ટોરેજ નથી. ખાતરી કરો કે તમારી પાસે 250MB ખાલી સ્થાન છે અને ફરીથી પ્રારંભ કરો."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index e29deab..3e37771 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"अनुपलब्ध व्यवस्थापक ऐप्लिकेशन के कारण कार्य प्रोफ़ाइल हटा दी गई"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"कार्य प्रोफ़ाइल व्यवस्थापक ऐप्लिकेशन या तो मौजूद नहीं है या वह खराब हो गया है. परिणामस्वरूप, आपकी कार्य प्रोफ़ाइल और उससे जुड़े डेटा को हटा दिया गया है. सहायता के लिए अपने व्यवस्थापक से संपर्क करें."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"आपकी कार्य प्रोफ़ाइल अब इस डिवाइस पर उपलब्‍ध नहीं है"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"डिवाइस प्रबंधित है"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"आपका संगठन इस डिवाइस का प्रबंधन करता है और वह नेटवर्क ट्रैफ़िक की निगरानी भी कर सकता है. विवरण के लिए टैप करें."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"आपके डिवाइस को मिटा दिया जाएगा"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"फ़ोन"</string>
     <string name="map" msgid="6068210738233985748">"मानचित्र"</string>
     <string name="browse" msgid="6993590095938149861">"ब्राउज़र"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"मैसेज (एसएमएस)"</string>
+    <string name="add_contact" msgid="7990645816259405444">"संपर्क"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"मेमोरी में जगह नहीं बची है"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"हो सकता है कुछ सिस्टम फ़ंक्शन कार्य न करें"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"सिस्टम के लिए ज़रूरी मेमोरी नहीं है. पक्का करें कि आपके पास 250एमबी की खाली जगह है और फिर से शुरू करें."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index bcf789b..3c295c8 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -174,6 +174,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Radni je profil izbrisan jer nedostaje administratorska aplikacija"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Administratorska aplikacija radnog profila nedostaje ili je oštećena. Zbog toga su radni profil i povezani podaci izbrisani. Za pomoć se obratite svom administratoru."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Vaš radni profil više nije dostupan na ovom uređaju"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Uređaj je upravljan"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Vaša organizacija upravlja ovim uređajem i može nadzirati mrežni promet. Dodirnite za pojedinosti."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Uređaj će se izbrisati"</string>
@@ -1000,10 +1002,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefon"</string>
     <string name="map" msgid="6068210738233985748">"Karte"</string>
     <string name="browse" msgid="6993590095938149861">"Preglednik"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke sistemske funkcije možda neće raditi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nema dovoljno pohrane za sustav. Oslobodite 250 MB prostora i pokrenite uređaj ponovo."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 93eb9ac..4e8d03c 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"A munkaprofilt a rendszer hiányzó rendszergazdai alkalmazás miatt törölte"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"A munkaprofil rendszergazdai alkalmazása hiányzik vagy sérült. A rendszer ezért törölte a munkaprofilt, és az ahhoz kapcsolódó adatokat. Ha segítségre van szüksége, vegye fel a kapcsolatot rendszergazdájával."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Munkaprofilja már nem hozzáférhető ezen az eszközön."</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Felügyelt eszköz"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Ezt az eszközt szervezete kezeli, és lehetséges, hogy a hálózati forgalmat is figyelik. További részletekért koppintson."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"A rendszer törölni fogja eszközét"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefon"</string>
     <string name="map" msgid="6068210738233985748">"Térkép"</string>
     <string name="browse" msgid="6993590095938149861">"Böngésző"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Névjegy"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kevés a szabad terület"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Előfordulhat, hogy néhány rendszerfunkció nem működik."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nincs elegendő tárhely a rendszerhez. Győződjön meg arról, hogy rendelkezik 250 MB szabad területtel, majd kezdje elölről."</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index e5d92c6..a5fbe34f 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Աշխատանքային պրոֆիլը ջնջվել է ադմինիստրատորի հավելվածի բացակայության պատճառով"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Աշխատանքային պրոֆիլի ադմինիստրատորի հավելվածը բացակայում է կամ վնասված է: Արդյունքում ձեր աշխատանքային պրոֆիլը և առնչվող տվյալները ջնջվել են: Օգնության համար դիմեք ձեր ադմինիստրատորին:"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Ձեր աշխատանքային պրոֆիլն այս սարքում այլևս հասանելի չէ"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Գաղտնաբառը մուտքագրելու չափից շատ փորձեր են կատարվել"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Սարքը կառավարվում է"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Ձեր կազմակերպությունը կառավարում է այս սարքը և կարող է վերահսկել ցանցի թրաֆիկը: Հպեք՝ մանրամասները դիտելու համար:"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Ձեր սարքը ջնջվելու է"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Հեռախոս"</string>
     <string name="map" msgid="6068210738233985748">"Քարտեզներ"</string>
     <string name="browse" msgid="6993590095938149861">"Դիտարկիչ"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Կոնտակտ"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Հիշողությունը սպառվում է"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Որոշ գործառույթներ կարող են չաշխատել"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Համակարգի համար բավարար հիշողություն չկա: Համոզվեք, որ ունեք 250ՄԲ ազատ տարածություն և վերագործարկեք:"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 89e96f9..173ad40 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -149,7 +149,7 @@
     <string name="httpErrorRedirectLoop" msgid="8679596090392779516">"Laman ini berisi terlalu banyak pengalihan server."</string>
     <string name="httpErrorUnsupportedScheme" msgid="5015730812906192208">"Protokol tidak didukung."</string>
     <string name="httpErrorFailedSslHandshake" msgid="96549606000658641">"Tidak dapat membuat sambungan aman."</string>
-    <string name="httpErrorBadUrl" msgid="3636929722728881972">"Tidak dapat membuka laman karena URL tidak valid."</string>
+    <string name="httpErrorBadUrl" msgid="3636929722728881972">"Tidak dapat membuka halaman karena URL tidak valid."</string>
     <string name="httpErrorFile" msgid="2170788515052558676">"Tidak dapat mengakses file."</string>
     <string name="httpErrorFileNotFound" msgid="6203856612042655084">"Tidak dapat menemukan file yang diminta."</string>
     <string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Terlalu banyak permintaan yang diproses. Coba lagi nanti."</string>
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil kerja dihapus karena tidak ada aplikasi admin"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplikasi admin profil kerja tidak ada atau rusak. Akibatnya, profil kerja dan data terkait telah dihapus. Hubungi admin untuk meminta bantuan."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Profil kerja tidak tersedia lagi di perangkat ini"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Terlalu banyak percobaan memasukkan sandi"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Perangkat ini ada yang mengelola"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Organisasi mengelola perangkat ini dan mungkin memantau traffic jaringan. Tap untuk melihat detailnya."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Perangkat akan dihapus"</string>
@@ -805,7 +806,7 @@
     <string name="js_dialog_before_unload_title" msgid="2619376555525116593">"Konfirmasi Navigasi"</string>
     <string name="js_dialog_before_unload_positive_button" msgid="3112752010600484130">"Keluar dari Laman ini"</string>
     <string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"Tetap di Laman ini"</string>
-    <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nYakin ingin beranjak dari laman ini?"</string>
+    <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nYakin ingin beranjak dari halaman ini?"</string>
     <string name="save_password_label" msgid="6860261758665825069">"Konfirmasi"</string>
     <string name="double_tap_toast" msgid="4595046515400268881">"Kiat: Ketuk dua kali untuk memperbesar dan memperkecil."</string>
     <string name="autofill_this_form" msgid="4616758841157816676">"IsiOtomatis"</string>
@@ -842,7 +843,7 @@
     <string name="save_password_notnow" msgid="6389675316706699758">"Tidak sekarang"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Ingat"</string>
     <string name="save_password_never" msgid="8274330296785855105">"Jangan"</string>
-    <string name="open_permission_deny" msgid="7374036708316629800">"Anda tidak memiliki izin untuk membuka laman ini."</string>
+    <string name="open_permission_deny" msgid="7374036708316629800">"Anda tidak memiliki izin untuk membuka halaman ini."</string>
     <string name="text_copied" msgid="4985729524670131385">"Teks disalin ke papan klip."</string>
     <string name="more_item_label" msgid="4650918923083320495">"Lainnya"</string>
     <string name="prepend_shortcut_label" msgid="2572214461676015642">"Menu+"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Telepon"</string>
     <string name="map" msgid="6068210738233985748">"Peta"</string>
     <string name="browse" msgid="6993590095938149861">"Browser"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontak"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang penyimpanan hampir habis"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak dapat bekerja"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Penyimpanan tidak cukup untuk sistem. Pastikan Anda memiliki 250 MB ruang kosong, lalu mulai ulang."</string>
@@ -1318,7 +1317,7 @@
     <string name="next_button_label" msgid="1080555104677992408">"Selanjutnya"</string>
     <string name="skip_button_label" msgid="1275362299471631819">"Lewati"</string>
     <string name="no_matches" msgid="8129421908915840737">"Tidak ada kecocokan"</string>
-    <string name="find_on_page" msgid="1946799233822820384">"Temukan pada laman"</string>
+    <string name="find_on_page" msgid="1946799233822820384">"Temukan pada halaman"</string>
     <plurals name="matches_found" formatted="false" msgid="1210884353962081884">
       <item quantity="other"><xliff:g id="INDEX">%d</xliff:g> dari <xliff:g id="TOTAL">%d</xliff:g></item>
       <item quantity="one">1 kecocokan</item>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 2850fec..c61c26e 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Vinnusniði eytt vegna þess að stjórnunarforrit vantar"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Stjórnunarforrit vinnusniðsins vantar eða er skemmt. Vinnusniðinu og gögnum því tengdu hefur því verið eytt. Hafðu samband við kerfisstjórann til að fá frekari aðstoð."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Vinnusniðið þitt er ekki lengur í boði á þessu tæki"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Tækinu er stjórnað"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Fyrirtækið þitt stjórnar þessu tæki og kann að fylgjast með netnotkun. Ýttu hér til að fá upplýsingar."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Tækið verður hreinsað"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Sími"</string>
     <string name="map" msgid="6068210738233985748">"Kort"</string>
     <string name="browse" msgid="6993590095938149861">"Vafri"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS-skilaboð"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Tengiliður"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Geymslurýmið er senn á þrotum"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sumir kerfiseiginleikar kunna að vera óvirkir"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Ekki nægt geymslurými fyrir kerfið. Gakktu úr skugga um að 250 MB séu laus og endurræstu."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 0274602..7e54a9c7 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Profilo di lavoro eliminato per app di amministrazione mancante"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"L\'app di amministrazione dei profili di lavoro manca o è danneggiata. Di conseguenza, il tuo profilo di lavoro e i relativi dati sono stati eliminati. Contatta l\'amministratore per ricevere assistenza."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Il tuo profilo di lavoro non è più disponibile sul dispositivo"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Troppi tentativi di inserimento della password"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Il dispositivo è gestito"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Questo dispositivo è gestito dalla tua organizzazione, che potrebbe monitorare il traffico di rete. Tocca per i dettagli."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Il dispositivo verrà resettato"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefono"</string>
     <string name="map" msgid="6068210738233985748">"Mappe"</string>
     <string name="browse" msgid="6993590095938149861">"Browser"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contatto"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spazio di archiviazione in esaurimento"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Alcune funzioni di sistema potrebbero non funzionare"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Memoria insufficiente per il sistema. Assicurati di avere 250 MB di spazio libero e riavvia."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 5fa6c52..23b8e38 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -176,6 +176,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"פרופיל העבודה נמחק מפני שחסרה אפליקציית ניהול"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"אפליקציית הניהול של פרופיל העבודה חסרה או פגומה. כתוצאה מכך, פרופיל העבודה שלך נמחק, כולל כל הנתונים הקשורים אליו. לקבלת עזרה, פנה למנהל המערכת."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"פרופיל העבודה שלך אינו זמין עוד במכשיר הזה"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"המכשיר מנוהל"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"הארגון שלך מנהל מכשיר זה ועשוי לנטר את התנועה ברשת. הקש לקבלת פרטים."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"תתבצע מחיקה של המכשיר"</string>
@@ -1020,10 +1022,8 @@
     <string name="dial" msgid="4204975095406423102">"טלפון"</string>
     <string name="map" msgid="6068210738233985748">"מפות"</string>
     <string name="browse" msgid="6993590095938149861">"דפדפן"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"איש קשר"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"שטח האחסון אוזל"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"ייתכן שפונקציות מערכת מסוימות לא יפעלו"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"‏אין מספיק שטח אחסון עבור המערכת. ודא שיש לך שטח פנוי בגודל 250MB התחל שוב."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 78e59a3..2100470 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"管理アプリがないため仕事用プロファイルが削除されました"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"仕事用プロファイルの管理アプリがないか、破損しています。そのため仕事用プロファイルと関連データが削除されました。管理者にサポートをご依頼ください。"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"お使いの仕事用プロファイルはこの端末で使用できなくなりました"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"管理対象の端末"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"この端末は組織によって管理され、ネットワーク トラフィックが監視される場合があります。詳しくはタップしてください。"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"端末のデータが消去されます"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"電話"</string>
     <string name="map" msgid="6068210738233985748">"マップ"</string>
     <string name="browse" msgid="6993590095938149861">"ブラウザ"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"連絡先"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"空き容量わずか"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"一部のシステム機能が動作しない可能性があります"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"システムに十分な容量がありません。250MBの空き容量を確保して再起動してください。"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index d9947b0..ee8daad 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"სამსახურის პროფილი წაიშალა ადმინისტრატორის აპის არქონის გამო"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"სამსახურის პროფილის ადმინისტრატორის აპი მიუწვდომელია ან დაზიანებულია. ამის გამო, თქვენი სამსახურის პროფილი და დაკავშირებული მონაცემები წაიშალა. დახმარებისთვის დაუკავშირდით თქვენს ადმინისტრატორს."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"თქვენი სამსახურის პროფილი აღარ არის ხელმისაწვდომი ამ მოწყობილობაზე"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"დაფიქსირდა პაროლის შეყვანის ზედმეტად ბევრი მცდელობა"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"მოწყობილობა მართულია"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"ამ მოწყობილობას თქვენი ორგანიზაცია მართავს და მას ქსელის ტრაფიკის მონიტორინგი შეუძლია. შეეხეთ დამატებითი დეტალებისთვის."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"თქვენი მოწყობილობა წაიშლება"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"ტელეფონი"</string>
     <string name="map" msgid="6068210738233985748">"Maps"</string>
     <string name="browse" msgid="6993590095938149861">"ბრაუზერი"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"კონტაქტი"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"თავისუფალი ადგილი იწურება"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"სისტემის ზოგიერთმა ფუნქციამ შესაძლოა არ იმუშავოს"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"სისტემისათვის საკმარისი საცავი არ არის. დარწმუნდით, რომ იქონიოთ სულ მცირე 250 მბაიტი თავისუფალი სივრცე და დაიწყეთ ხელახლა."</string>
@@ -993,8 +992,7 @@
     <string name="cancel" msgid="6442560571259935130">"გაუქმება"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
     <string name="no" msgid="5141531044935541497">"გაუქმება"</string>
-    <!-- no translation found for close (2318214661230355730) -->
-    <skip />
+    <string name="close" msgid="2318214661230355730">"დახურვა"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"ყურადღება"</string>
     <string name="loading" msgid="7933681260296021180">"ჩატვირთვა…"</string>
     <string name="capital_on" msgid="1544682755514494298">"ჩართ."</string>
@@ -1051,10 +1049,8 @@
     <string name="screen_compat_mode_scale" msgid="3202955667675944499">"მასშტაბი"</string>
     <string name="screen_compat_mode_show" msgid="4013878876486655892">"ყოველთვის ჩვენება"</string>
     <string name="screen_compat_mode_hint" msgid="1064524084543304459">"ხელახალი გააქტიურება განყოფილებაში: სისტემის პარამეტრები &gt; აპები &gt; ჩამოტვირთულები."</string>
-    <!-- no translation found for top_app_killed_title (6814231368167994497) -->
-    <skip />
-    <!-- no translation found for top_app_killed_message (3487519022191609844) -->
-    <skip />
+    <string name="top_app_killed_title" msgid="6814231368167994497">"აპი არ რეაგირებს"</string>
+    <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> შესაძლოა მეხსიერებას გადამეტებით იყენებდეს."</string>
     <string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის მიერ ეკრანის ამჟამინდელი პარამეტრები მხარდაუჭერელია და შეიძლება არასათანადოდ იმუშაოს."</string>
     <string name="unsupported_display_size_show" msgid="7969129195360353041">"ყოველთვის ჩვენება"</string>
     <string name="smv_application" msgid="3307209192155442829">"აპმა <xliff:g id="APPLICATION">%1$s</xliff:g> (პროცესი <xliff:g id="PROCESS">%2$s</xliff:g>) დაარღვია საკუთარი StrictMode დებულება."</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 95e1459..bbf1b4a9 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Әкімші қолданбасы болмағандықтан жұмыс профилі жойылды"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Жұмыс профилінің әкімші қолданбасы жоқ немесе бүлінген. Нәтижесінде жұмыс профиліңіз және қатысты деректер жойылды. Көмек алу үшін әкімшіге хабарласыңыз."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Жұмыс профиліңіз осы құрылғыда енді қолжетімді емес"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Құрылғы басқарылады"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Ұйымыңыз осы құрылғыны басқарады және желі трафигін бақылауы мүмкін. Мәліметтер алу үшін түртіңіз."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Құрылғыңыздағы деректер өшіріледі"</string>
@@ -968,7 +970,7 @@
     <string name="delete" msgid="6098684844021697789">"Жою"</string>
     <string name="copyUrl" msgid="2538211579596067402">"URL мекенжайын көшіру"</string>
     <string name="selectTextMode" msgid="1018691815143165326">"Мәтінді бөлектеу"</string>
-    <string name="undo" msgid="7905788502491742328">"Кері қайтару"</string>
+    <string name="undo" msgid="7905788502491742328">"Қайтару"</string>
     <string name="redo" msgid="7759464876566803888">"Қайтару"</string>
     <string name="autofill" msgid="3035779615680565188">"Aвтотолтыру"</string>
     <string name="textSelectionCABTitle" msgid="5236850394370820357">"Мәтін таңдау"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Телефон"</string>
     <string name="map" msgid="6068210738233985748">"Maps"</string>
     <string name="browse" msgid="6993590095938149861">"Браузер"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Контакт"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Жадта орын азайып барады"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Жүйенің кейбір функциялары жұмыс істемеуі мүмкін"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Жүйе үшін жад жеткіліксіз. 250 МБ бос орын бар екенін тексеріп, қайта іске қосыңыз."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index ae11db4..128c3f3 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"កម្រងព័ត៌មាន​ការងារ​ត្រូវបាន​លុប​ដោយសារ​បាត់​កម្មវិធី​អ្នកគ្រប់គ្រង"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"កម្មវិធី​អ្នកគ្រប់គ្រង​កម្រងព័ត៌មាន​ការងារនេះ​អាច​បាត់ ឬ​មាន​បញ្ហា។ ដូច្នេះហើយ​ទើប​កម្រងព័ត៌មាន​ការងារ​របស់អ្នក និង​ទិន្នន័យ​ដែល​ពាក់ព័ន្ធត្រូវ​បានលុប។ សូមទាក់ទង​ទៅអ្នក​គ្រប់គ្រង​របស់អ្នក ដើម្បី​ទទួល​បាន​ជំនួយ។"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"កម្រងព័ត៌មាន​ការងារ​របស់អ្នក​លែងមាន​នៅលើ​ឧបករណ៍​នេះទៀត​ហើយ"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"ឧបករណ៍ស្ថិតក្រោមការគ្រប់គ្រង"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"ស្ថាប័នរបស់អ្នកគ្រប់គ្រងឧបករណ៍នេះ ហើយអាចនឹងតាមដានចរាចរណ៍បណ្តាញ។ ចុចដើម្បីទទួលបានព័ត៌មានលម្អិត។"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"ឧបករណ៍របស់អ្នកនឹងត្រូវបានលុប"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"ទូរសព្ទ"</string>
     <string name="map" msgid="6068210738233985748">"ផែនទី"</string>
     <string name="browse" msgid="6993590095938149861">"កម្មវិធីរុករកតាមអ៊ីនធឺណិត"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"ទំនាក់​ទំនង"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"អស់​ទំហំ​ផ្ទុក"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារ​ប្រព័ន្ធ​មួយ​ចំនួន​អាច​មិន​ដំណើរការ​"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"មិន​មាន​ទំហំ​ផ្ទុក​​គ្រប់​គ្រាន់​សម្រាប់​ប្រព័ន្ធ​។ សូម​ប្រាកដ​ថា​អ្នក​មាន​ទំហំ​ទំនេរ​ 250MB ហើយ​ចាប់ផ្ដើម​ឡើង​វិញ។"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 125ccbb..eaeb25b 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"ನಿರ್ವಾಹಕ ಅಪ್ಲಿಕೇಶನ್‌ ತಪ್ಪಿಹೋಗಿರುವುದರಿಂದ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ ಅನ್ನು ಅಳಿಸಲಾಗಿದೆ"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ ನಿರ್ವಾಹಕ ಅಪ್ಲಿಕೇಶನ್ ಕಳೆದು ಹೋಗಿದೆ ಅಥವಾ ಹಾಳಾಗಿದೆ. ಇದರ ಪರಿಣಾಮವಾಗಿ ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ ಮತ್ತು ಅದಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗಿದೆ. ಸಹಾಯಕ್ಕಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ ಈ ಸಾಧನದಲ್ಲಿ ಈಗ ಲಭ್ಯವಿಲ್ಲ"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"ಸಾಧನವನ್ನು ನಿರ್ವಹಿಸಲಾಗುತ್ತಿದೆ"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಈ ಸಾಧನವನ್ನು ನಿರ್ವಹಿಸುತ್ತದೆ ಮತ್ತು ಅದು ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಮೇಲೆ ಗಮನವಿರಿಸಬಹುದು. ವಿವರಗಳಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"ನಿಮ್ಮ ಸಾಧನವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"ಫೋನ್"</string>
     <string name="map" msgid="6068210738233985748">"ನಕ್ಷೆಗಳು"</string>
     <string name="browse" msgid="6993590095938149861">"ಬ್ರೌಸರ್"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"ಎಸ್‌ಎಂಎಸ್‌"</string>
+    <string name="add_contact" msgid="7990645816259405444">"ಸಂಪರ್ಕ"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"ಸಂಗ್ರಹಣೆ ಸ್ಥಳವು ತುಂಬಿದೆ"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"ಕೆಲವು ಸಿಸ್ಟಂ ಕಾರ್ಯವಿಧಾನಗಳು ಕಾರ್ಯನಿರ್ವಹಿಸದೇ ಇರಬಹುದು"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ಸಿಸ್ಟಂನಲ್ಲಿ ಸಾಕಷ್ಟು ಸಂಗ್ರಹಣೆಯಿಲ್ಲ. ನೀವು 250MB ನಷ್ಟು ಖಾಲಿ ಸ್ಥಳವನ್ನು ಹೊಂದಿರುವಿರಾ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಹಾಗೂ ಮರುಪ್ರಾರಂಭಿಸಿ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index c253742..59fa0bf 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"관리 앱이 없어서 직장 프로필이 삭제되었습니다."</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"직장 프로필 관리 앱이 없거나 손상되어 직장 프로필 및 관련 데이터가 삭제되었습니다. 도움이 필요한 경우 관리자에게 문의하세요."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"직장 프로필을 이 기기에서 더 이상 사용할 수 없습니다."</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"관리되는 기기"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"조직에서 이 기기를 관리하며 네트워크 트래픽을 모니터링할 수도 있습니다. 자세한 내용을 보려면 탭하세요."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"기기가 삭제됩니다."</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"전화"</string>
     <string name="map" msgid="6068210738233985748">"지도"</string>
     <string name="browse" msgid="6993590095938149861">"브라우저"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"연락처"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"저장 공간이 부족함"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"일부 시스템 기능이 작동하지 않을 수 있습니다."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"시스템의 저장 공간이 부족합니다. 250MB의 여유 공간이 확보한 후 다시 시작하세요."</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 77f3b0c..fb7b4f6 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Башкаруучу колдонмосу болбогондуктан, жумуш профили жок кылынды"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Жумуш профилинин башкаруучу колдонмосу жок же бузулгандыктан, жумуш профилиңиз жана ага байланыштуу дайындар жок кылынды. Жардам алуу үчүн администраторуңузга кайрылыңыз."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Жумуш профилиңиз бул түзмөктөн жок кылынды"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Түзмөктү ишкана башкарат"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Ишканаңыз бул түзмөктү башкарат жана тармак трафигин көзөмөлдөшү мүмкүн. Чоо-жайын көрүү үчүн таптап коюңуз."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Түзмөгүңүз тазаланат"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Телефон"</string>
     <string name="map" msgid="6068210738233985748">"Карталар"</string>
     <string name="browse" msgid="6993590095938149861">"Серепчи"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Байланыш"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Сактагычта орун калбай баратат"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Системанын кээ бир функциялары иштебеши мүмкүн"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Тутумда сактагыч жетишсиз. 250МБ бош орун бар экенин текшерип туруп, өчүрүп күйгүзүңүз."</string>
@@ -993,8 +993,7 @@
     <string name="cancel" msgid="6442560571259935130">"Жокко чыгаруу"</string>
     <string name="yes" msgid="5362982303337969312">"Жарайт"</string>
     <string name="no" msgid="5141531044935541497">"Жокко чыгаруу"</string>
-    <!-- no translation found for close (2318214661230355730) -->
-    <skip />
+    <string name="close" msgid="2318214661230355730">"ЖАБУУ"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"Көңүл буруңуз"</string>
     <string name="loading" msgid="7933681260296021180">"Жүктөлүүдө…"</string>
     <string name="capital_on" msgid="1544682755514494298">"ЖАНДЫРЫЛГАН"</string>
@@ -1051,10 +1050,8 @@
     <string name="screen_compat_mode_scale" msgid="3202955667675944499">"Шкала"</string>
     <string name="screen_compat_mode_show" msgid="4013878876486655892">"Ар дайым көрсөтүлсүн"</string>
     <string name="screen_compat_mode_hint" msgid="1064524084543304459">"Муну тутум жөндөөлөрүнөн кайра иштетүү &gt; Колдонмолор &gt; Жүктөлүп алынган."</string>
-    <!-- no translation found for top_app_killed_title (6814231368167994497) -->
-    <skip />
-    <!-- no translation found for top_app_killed_message (3487519022191609844) -->
-    <skip />
+    <string name="top_app_killed_title" msgid="6814231368167994497">"Колдонмо жооп бербей жатат"</string>
+    <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу эстутумду өтө көп колдонуп жатышы мүмкүн."</string>
     <string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу көрүнүштүн тандалган өлчөмүн экранда көрсөтө албайт жана туура эмес иштеши мүмкүн."</string>
     <string name="unsupported_display_size_show" msgid="7969129195360353041">"Ар дайым көрсөтүлсүн"</string>
     <string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосу (<xliff:g id="PROCESS">%2$s</xliff:g> процесси) өз алдынча иштеткен StrictMode саясатын бузду."</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index d53c702..477fbdc06 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"ລຶບໂປຣໄຟລ໌ບ່ອນເຮັດວຽກແລ້ວເນື່ອງຈາກບໍ່ມີແອັບຜູ້ເບິ່ງແຍງລະບົບ"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"ບໍ່ມີແອັບຜູ້ເບິ່ງແຍງລະບົບໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ ຫຼື ເສຍຫາຍ. ຜົນກໍຄື, ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ ແລະ ຂໍ້ມູນທີ່ກ່ຽວຂ້ອງຂອງທ່ານຖືກລຶບອອກແລ້ວ. ໃຫ້ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບການຊ່ວຍເຫຼືອ."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານບໍ່ສາມາດໃຊ້ໄດ້ໃນອຸປະກອນນີ້ອີກຕໍ່ໄປ"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"ລອງໃສ່ລະຫັດຜ່ານຫຼາຍເທື່ອເກີນໄປ"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"ອຸປະກອນມີການຈັດການ"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"ອົງກອນຂອງທ່ານຈັດການອຸປະກອນນີ້ ແລະ ອາດກວດສອບທຣາບຟິກເຄືອຂ່າຍນຳ. ແຕະເພື່ອເບິ່ງລາຍລະອຽດ."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"ອຸ​ປະ​ກອນ​ຂອງ​ທ່ານ​ຈະ​ຖືກ​ລຶບ"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"ໂທລະສັບ"</string>
     <string name="map" msgid="6068210738233985748">"ແຜນທີ່"</string>
     <string name="browse" msgid="6993590095938149861">"ໂປຣແກຣມທ່ອງເວັບ"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"ຕິດຕໍ່"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນກຳລັງຈະເຕັມ"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"ການເຮັດວຽກບາງຢ່າງຂອງລະບົບບາງອາດຈະໃຊ້ບໍ່ໄດ້"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"​ບໍ່​ມີ​ບ່ອນ​ເກັບ​ຂໍ້​ມູນ​ພຽງ​ພໍ​ສຳ​ລັບ​ລະ​ບົບ. ກວດ​ສອບ​ໃຫ້​ແນ່​ໃຈ​ວ່າ​ທ່ານ​ມີ​ພື້ນ​ທີ່​ຫວ່າງ​ຢ່າງ​ໜ້ອຍ 250MB ​ແລ້ວລອງ​ໃໝ່."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index a3deaa7..2e44ed2 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -176,6 +176,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Darbo profilis ištrintas dėl trūkstamos administratoriaus programos"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Trūksta darbo profilio administratoriaus programos arba ji sugadinta. Todėl darbo profilis ir susiję duomenys buvo ištrinti. Jei reikia pagalbos, susisiekite su administratoriumi."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Darbo profilis nebepasiekiamas šiame įrenginyje"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Įrenginys yra tvarkomas"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Šį įrenginį tvarko organizacija ir gali stebėti tinklo srautą. Palieskite, kad gautumėte daugiau informacijos."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Įrenginys bus ištrintas"</string>
@@ -1020,10 +1022,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefonas"</string>
     <string name="map" msgid="6068210738233985748">"Žemėlapiai"</string>
     <string name="browse" msgid="6993590095938149861">"Naršyklė"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontaktas"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Mažėja laisvos saugyklos vietos"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Kai kurios sistemos funkcijos gali neveikti"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistemos saugykloje nepakanka vietos. Įsitikinkite, kad yra 250 MB laisvos vietos, ir paleiskite iš naujo."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index c323fcc..659c9a7 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -174,6 +174,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Darba profils tika dzēsts, jo trūkst administratora lietotnes."</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Trūkst darba profila administratora lietotnes, vai šī lietotne ir bojāta. Šī iemesla dēļ jūsu darba profils un saistītie dati tika dzēsti. Lai saņemtu palīdzību, sazinieties ar administratoru."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Jūsu darba profils šai ierīcē vairs nav pieejams."</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Ierīce tiek pārvaldīta"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Jūsu organizācija pārvalda šo ierīci un var uzraudzīt tīkla datplūsmu. Pieskarieties, lai saņemtu detalizētu informāciju."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Jūsu ierīces dati tiks dzēsti"</string>
@@ -1000,10 +1002,8 @@
     <string name="dial" msgid="4204975095406423102">"Tālrunis"</string>
     <string name="map" msgid="6068210738233985748">"Kartes"</string>
     <string name="browse" msgid="6993590095938149861">"Pārlūkprogramma"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"Īsziņas"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontaktpersona"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Paliek maz brīvas vietas"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Dažas sistēmas funkcijas var nedarboties."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistēmai pietrūkst vietas. Atbrīvojiet vismaz 250 MB vietas un restartējiet ierīci."</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index c3de4a9..a98e2f0 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Работниот профил е избришан поради исчезнувањето на апликацијата на администратор"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Апликацијата на администраторот за работниот профил или исчезна или е оштетена. Како резултат на тоа, вашиот работен профил и поврзаните податоци ќе се избришат. За помош, контактирајте со администраторот."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Вашиот работен профил веќе не е достапен на уредов"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Премногу обиди за внесување лозинка"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Некој управува со уредот"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Вашата организација управува со уредов и можно е да го следи сообраќајот на мрежата. Допрете за детали."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Уредот ќе се избрише"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Телефон"</string>
     <string name="map" msgid="6068210738233985748">"„Карти“"</string>
     <string name="browse" msgid="6993590095938149861">"Прелистувач"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Контакт"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Меморијата е речиси полна"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некои системски функции може да не работат"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Нема доволно меморија во системот. Проверете дали има слободен простор од 250 МБ и рестартирајте."</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 2623167..65b7041 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"അഡ്‌മിൻ ആപ്പ് വിട്ടുപോയിരിക്കുന്നതിനാൽ ഔദ്യോഗിക പ്രൊഫൈൽ ഇല്ലാതാക്കി"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"ഔദ്യോഗിക പ്രൊഫൈൽ അഡ്‌മിൻ ആപ്പ് വിട്ടുപോയിരിക്കുന്നു അല്ലെങ്കിൽ കേടായിരിക്കുന്നു. ഫലമായി, നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലും ബന്ധപ്പെട്ട വിവരങ്ങളും ഇല്ലാതാക്കിയിരിക്കുന്നു. സഹായത്തിന് അഡ്‌മിനെ ബന്ധപ്പെടുക."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ഈ ഉപകരണത്തിൽ തുടർന്നങ്ങോട്ട് നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈൽ ലഭ്യമല്ല"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"ഉപകരണം മാനേജുചെയ്യുന്നുണ്ട്"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"നിങ്ങളുടെ സ്ഥാപനമാണ് ഈ ഉപകരണം മാനേജുചെയ്യുന്നത്, നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കുകയും ചെയ്തേക്കാം, വിശദാംശങ്ങൾ അറിയാൻ ടാപ്പുചെയ്യുക."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"നിങ്ങളുടെ ഉപകരണം മായ്‌ക്കും"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"ഫോണ്‍"</string>
     <string name="map" msgid="6068210738233985748">"മാപ്‌സ്"</string>
     <string name="browse" msgid="6993590095938149861">"ബ്രൗസർ"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"കോണ്‍‌ടാക്റ്റ്"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"സംഭരണയിടം കഴിഞ്ഞു"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"ചില സിസ്റ്റം പ്രവർത്തനങ്ങൾ പ്രവർത്തിക്കണമെന്നില്ല."</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"സിസ്‌റ്റത്തിനായി മതിയായ സംഭരണമില്ല. 250MB സൗജന്യ സംഭരണമുണ്ടെന്ന് ഉറപ്പുവരുത്തി പുനരാരംഭിക്കുക."</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index ce09140..b088180 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Админ апп байхгүй байгаа тул ажлын профайлыг устгасан байна"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Ажлын профайлын админ апп байхгүй эсвэл эвдэрсэн байна. Үүний улмаас таны ажлын профайл болон холбогдох мэдээллийг устгасан болно. Тусламж хэрэгтэй бол админтай холбогдоно уу."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Таны ажлын профайл энэ төхөөрөмжид боломжгүй байна"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Төхөөрөмжийг удирдсан"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Таны байгууллага энэ төхөөрөмжийг удирдаж, сүлжээний ачааллыг хянадаг. Дэлгэрэнгүй мэдээлэл авах бол товшино уу."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Таны төхөөрөмж устах болно."</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Утас"</string>
     <string name="map" msgid="6068210738233985748">"Газрын зураг"</string>
     <string name="browse" msgid="6993590095938149861">"Хөтөч"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Харилцагч"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Сангийн хэмжээ дутагдаж байна"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Зарим систем функц ажиллахгүй байна"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Системд хангалттай сан байхгүй байна. 250MБ чөлөөтэй зай байгаа эсэхийг шалгаад дахин эхлүүлнэ үү."</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 2d0cad7..9c74b30 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"प्रशासक अॅप गहाळ असल्यामुळे कार्य प्रोफाइल हटवले गेले"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"कार्य प्रोफाइल प्रशासक अॅप गहाळ आहे किंवा करप्ट आहे. परिणामी, आपले कार्य प्रोफाइल आणि संबंधित डेटा हटवले गेले आहेत. सहाय्यासाठी आपल्या प्रशासकाशी संपर्क साधा."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"आपले कार्य प्रोफाइल आता या डिव्हाइसवर उपलब्‍ध नाही"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"डिव्हाइस व्यवस्थापित केले आहे"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"आपली संस्था हे डिव्हाइस व्यवस्थापित करते आणि नेटवर्क रहदारीचे निरीक्षण करू शकते. तपशीलांसाठी टॅप करा."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"तुमचे डिव्हाइस मिटविले जाईल"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"फोन"</string>
     <string name="map" msgid="6068210738233985748">"नकाशे"</string>
     <string name="browse" msgid="6993590095938149861">"ब्राउझर"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"एसएमएस"</string>
+    <string name="add_contact" msgid="7990645816259405444">"संपर्क"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"संचयन स्थान संपत आहे"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"काही सिस्टम कार्ये कार्य करू शकत नाहीत"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"सिस्टीमसाठी पुरेसे संचयन नाही. आपल्याकडे 250MB मोकळे स्थान असल्याचे सुनिश्चित करा आणि रीस्टार्ट करा."</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 536f2c2..a81b76f 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil kerja dipadamkan kerana ketiadaan apl pentadbir"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Apl pentadbir profil kerja tiada atau rosak. Akibatnya, profil kerja anda dan data yang berkaitan telah dipadamkan. Hubungi pentadbir anda untuk mendapatkan bantuan."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Profil kerja anda tidak lagi tersedia pada peranti ini"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Peranti ini diurus"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Organisasi anda mengurus peranti ini dan mungkin memantau trafik rangkaian. Ketik untuk mendapatkan butiran."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Peranti anda akan dipadam"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefon"</string>
     <string name="map" msgid="6068210738233985748">"Peta"</string>
     <string name="browse" msgid="6993590095938149861">"Penyemak imbas"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kenalan"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang storan semakin berkurangan"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak berfungsi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Tidak cukup storan untuk sistem. Pastikan anda mempunyai 250MB ruang kosong dan mulakan semula."</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index c0539a5..34e783b 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"စီမံခန့်ခွဲရန် အက်ပ်မရှိသောကြောင့် အလုပ်ပရိုဖိုင်ကို ဖျက်လိုက်ခြင်းဖြစ်သည်"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"အလုပ်ပရိုဖိုင် စီမံခန့်ခွဲရန်အက်ပ် မရှိပါ သို့မဟုတ် ပျက်စီးနေပါသည်။ ထို့ကြောင့် သင်၏ အလုပ်ပရိုဖိုင်နှင့် ဆက်စပ်နေသော ဒေတာများကို ဖျက်လိုက်ပါပြီ။ အကူအညီရယူရန် သင်၏စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ဤစက်ပစ္စည်းတွင် သင်၏ အလုပ်ပရိုဖိုင်မရှိတော့ပါ"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"စက်ပစ္စည်းကို စီမံခန့်ခွဲထားပါသည်"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"ဤစက်ပစ္စည်းကို သင်၏ အဖွဲ့အစည်းက စီမံပြီး ကွန်ရက်အသွားအလာကို စောင့်ကြည့်နိုင်ပါသည်။ ထပ်မံလေ့လာရန် တို့ပါ။"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"သင့်ကိရိယာအား ပယ်ဖျက်လိမ့်မည်"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"ဖုန်း"</string>
     <string name="map" msgid="6068210738233985748">"Maps"</string>
     <string name="browse" msgid="6993590095938149861">"ဘရောင်ဇာ"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS စာတိုစနစ်"</string>
+    <string name="add_contact" msgid="7990645816259405444">"အဆက်အသွယ်"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"သိမ်းဆည်သော နေရာ နည်းနေပါသည်"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"တချို့ စနစ်လုပ်ငန်းများ အလုပ် မလုပ်ခြင်း ဖြစ်နိုင်ပါသည်"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"စနစ်အတွက် သိုလှောင်ခန်း မလုံလောက်ပါ။ သင့်ဆီမှာ နေရာလွတ် ၂၅၀ MB ရှိတာ စစ်ကြည့်ပြီး စတင်ပါ။"</string>
@@ -1187,7 +1187,7 @@
     <string name="no_permissions" msgid="7283357728219338112">"ခွင့်ပြုချက်မလိုအပ်ပါ"</string>
     <string name="perm_costs_money" msgid="4902470324142151116">"သင့်အတွက် ပိုက်ဆံကုန်ကျနိုင်ပါသည်"</string>
     <string name="dlg_ok" msgid="7376953167039865701">"အိုကေ"</string>
-    <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB ဖြင့်ဤစက်ပစ္စည်းကို အားသွင်းနေသည်"</string>
+    <string name="usb_charging_notification_title" msgid="6895185153353640787">"ဤစက်ကို USB ဖြင့် အားသွင်းနေသည်"</string>
     <string name="usb_supplying_notification_title" msgid="5310642257296510271">"ချိတ်ဆက်ထားသည့် စက်ပစ္စည်းကို USB မှတစ်ဆင့် အားသွင်းနေသည်"</string>
     <string name="usb_mtp_notification_title" msgid="8396264943589760855">"ဖိုင်လွှဲပြောင်းရန်အတွက် USB"</string>
     <string name="usb_ptp_notification_title" msgid="1347328437083192112">"ဓာတ်ပုံလွှဲပြောင်းရန်အတွက် USB"</string>
@@ -1196,8 +1196,8 @@
     <string name="usb_notification_message" msgid="3370903770828407960">"နောက်ထပ်ရွေးချယ်စရာများအတွက် တို့ပါ။"</string>
     <string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"အန်နာလော့ အသံကိရိယာကို တွေ့ထားပါသည်"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"တပ်ဆင်ထားသော ကိရိယာကို ဤဖုန်းနှင့် တွဲသုံး၍မရပါ။ ပိုမိုလေ့လာရန် တို့ပါ။"</string>
-    <string name="adb_active_notification_title" msgid="6729044778949189918">"USB အမှားစစ်ခြင်းအား ချိတ်ဆက်ထားသည်"</string>
-    <string name="adb_active_notification_message" msgid="4948470599328424059">"USB ဆက်သွယ်ရေးစနစ်ကို ပိတ်ရန် တို့ပါ။"</string>
+    <string name="adb_active_notification_title" msgid="6729044778949189918">"USB အမှားရှာပြင်စနစ် ချိတ်ဆက်ထားသည်"</string>
+    <string name="adb_active_notification_message" msgid="4948470599328424059">"USB အမှားရှာပြင်စနစ် ပိတ်ရန် တို့ပါ။"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ဖြင့် အမှားရှာပြင်ခြင်းကို ပိတ်ရန် ရွေးပါ။"</string>
     <string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"ချွတ်ယွင်းချက် အစီရင်ခံစာပြုစုနေသည်..."</string>
     <string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"ချွတ်ယွင်းချက် အစီရင်ခံစာကို မျှဝေမလား။"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 4eb0f1d..52436ea 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Jobbprofilen er slettet på grunn av manglende administratorapp"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Administratorappen for jobbprofilen mangler eller er skadet. Dette har ført til at jobbprofilen og alle data knyttet til den, har blitt slettet. Ta kontakt med administratoren for å få hjelp."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Jobbprofilen din er ikke lenger tilgjengelig på denne enheten"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Enheten administreres"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Organisasjonen din kontrollerer denne enheten og kan overvåke nettverkstrafikk. Trykk for å få mer informasjon."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Enheten blir slettet"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefon"</string>
     <string name="map" msgid="6068210738233985748">"Kart"</string>
     <string name="browse" msgid="6993590095938149861">"Nettleser"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lite ledig lagringsplass"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Enkelte systemfunksjoner fungerer muligens ikke slik de skal"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Det er ikke nok lagringsplass for systemet. Kontrollér at du har 250 MB ledig plass, og start på nytt."</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 16743d5..bb84eae 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"प्रशासकीय अनुप्रयोग नभएकाले कार्य प्रोफाइल मेटाइयो"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"उक्त कार्य प्रोफाइलको प्रशासकीय अनुप्रयोग छैन वा बिग्रेको छ। त्यसले गर्दा, तपाईंको कार्य प्रोफाइल र सम्बन्धित डेटालाई मेटिएको छ। सहायताका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्।"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"तपाईंको कार्य प्रोफाइल अब उप्रान्त यस यन्त्रमा उपलब्ध छैन"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"यन्त्र व्यवस्थित गरिएको छ"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"तपाईंको संगठनले यस यन्त्रको व्यवस्थापन गर्दछ र नेटवर्क ट्राफिकको अनुगमन गर्न सक्छ। विवरणहरूका लागि ट्याप गर्नुहोस्।"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"तपाईंको यन्त्र मेटिनेछ"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"फोन गर्नुहोस्"</string>
     <string name="map" msgid="6068210738233985748">"नक्सा"</string>
     <string name="browse" msgid="6993590095938149861">"ब्राउजर"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"सम्पर्क"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"भण्डारण ठाउँ सकिँदै छ"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"सायद केही प्रणाली कार्यक्रमहरूले काम गर्दैनन्"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"प्रणालीको लागि पर्याप्त भण्डारण छैन। तपाईँसँग २५० मेगा बाइट ठाउँ खाली भएको निश्चित गर्नुहोस् र फेरि सुरु गर्नुहोस्।"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index e914caf..3f8ed59 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Werkprofiel verwijderd vanwege ontbrekende beheer-app"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"De beheer-app van het werkprofiel ontbreekt of is beschadigd. Als gevolg hiervan zijn je werkprofiel en alle gerelateerde gegevens verwijderd. Neem contact op met je beheerder voor hulp."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Je werkprofiel is niet meer beschikbaar op dit apparaat"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Te veel wachtwoordpogingen"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Apparaat wordt beheerd"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Dit apparaat wordt beheerd door je organisatie. Het netwerkverkeer kan worden bijgehouden. Tik voor meer informatie."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Je apparaat wordt gewist"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefoon"</string>
     <string name="map" msgid="6068210738233985748">"Kaarten"</string>
     <string name="browse" msgid="6993590095938149861">"Browser"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"Sms"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Opslagruimte is bijna vol"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bepaalde systeemfuncties werken mogelijk niet"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Onvoldoende opslagruimte voor het systeem. Zorg ervoor dat je 250 MB vrije ruimte hebt en start opnieuw."</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 7917b23..2c0ba4b 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"ਗੁੰਮਸ਼ੁਦਾ ਪ੍ਰਸ਼ਾਸਕ ਐਪ ਦੇ ਕਾਰਨ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਮਿਟਾਇਆ ਗਿਆ"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਪ੍ਰਸ਼ਾਸਕ ਐਪ ਜਾਂ ਤਾਂ ਗੁੰਮਸ਼ੁਦਾ ਹੈ ਜਾਂ ਖਰਾਬ ਹੈ। ਨਤੀਜੇ ਵਜੋਂ, ਤੁਹਾਡੀ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਅਤੇ ਸਬੰਧਿਤ ਡਾਟਾ ਮਿਟਾਇਆ ਗਿਆ ਹੈ। ਸਹਾਇਤਾ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ਤੁਹਾਡਾ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਹੁਣ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਨ ਅਧੀਨ ਹੈ"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"ਤੁਹਾਡਾ ਸੰਗਠਨ ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਦਾ ਹੈ ਅਤੇ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦਾ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"ਤੁਹਾਡਾ ਡੀਵਾਈਸ ਮਿਟਾਇਆ ਜਾਏਗਾ"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"ਫ਼ੋਨ ਕਰੋ"</string>
     <string name="map" msgid="6068210738233985748">"ਨਕਸ਼ੇ"</string>
     <string name="browse" msgid="6993590095938149861">"ਬ੍ਰਾਊਜ਼ਰ"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"ਸੰਪਰਕ"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"ਸਟੋਰੇਜ ਦੀ ਜਗ੍ਹਾ ਖਤਮ ਹੋ ਰਹੀ ਹੈ"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"ਕੁਝ ਸਿਸਟਮ ਫੰਕਸ਼ਨ ਕੰਮ ਨਹੀਂ ਵੀ ਕਰ ਸਕਦੇ"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ਸਿਸਟਮ ਲਈ ਲੋੜੀਂਦੀ ਸਟੋਰੇਜ ਨਹੀਂ ਹੈ। ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਹਾਡੇ ਕੋਲ 250MB ਖਾਲੀ ਜਗ੍ਹਾ ਹੈ ਅਤੇ ਮੁੜ-ਚਾਲੂ ਕਰੋ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 0989576..685ad53 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -176,6 +176,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil do pracy został usunięty z powodu braku aplikacji administratora"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Brakuje aplikacji administratora profilu do pracy lub jest ona uszkodzona. Dlatego Twój profil do pracy i związane z nim dane zostały usunięte. Skontaktuj się ze swoim administratorem, by uzyskać pomoc."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Twój profil do pracy nie jest już dostępny na tym urządzeniu"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Urządzenie jest zarządzane"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Twoja organizacja zarządza tym urządzeniem i może monitorować ruch w sieci. Kliknij, by dowiedzieć się więcej."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Twoje urządzenie zostanie wyczyszczone"</string>
@@ -1020,10 +1022,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefon"</string>
     <string name="map" msgid="6068210738233985748">"Mapy"</string>
     <string name="browse" msgid="6993590095938149861">"Internet"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kończy się miejsce"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektóre funkcje systemu mogą nie działać"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Za mało pamięci w systemie. Upewnij się, że masz 250 MB wolnego miejsca i uruchom urządzenie ponownie."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index a9e0ac9..1a9eefb 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Perfil de trabalho excluído devido à ausência de um app para administrador"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"O app para administrador do perfil de trabalho não foi encontrado ou está corrompido. Consequentemente, seu perfil de trabalho e os dados relacionados foram excluídos. Entre em contato com seu administrador para receber assistência."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Seu perfil de trabalho não está mais disponível neste dispositivo"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Muitas tentativas de senha"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"O dispositivo é gerenciado"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Sua organização gerencia este dispositivo e pode monitorar o tráfego de rede. Toque para ver detalhes."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Seu dispositivo será limpo"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefone"</string>
     <string name="map" msgid="6068210738233985748">"Mapas"</string>
     <string name="browse" msgid="6993590095938149861">"Navegador"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contato"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Pouco espaço de armazenamento"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema podem não funcionar"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index cd26bd5..0f4635b 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Perfil de trabalho eliminado devido a aplicação de administração em falta"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"A aplicação de administração do perfil de trabalho está em falta ou danificada. Consequentemente, o seu perfil de trabalho e os dados relacionados foram eliminados. Contacte o administrador para obter assistência."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"O seu perfil de trabalho já não está disponível neste dispositivo"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Demasiadas tentativas de introdução da palavra-passe"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"O dispositivo é gerido"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"A sua entidade gere este dispositivo e pode monitorizar o tráfego de rede. Toque para obter mais detalhes."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"O seu dispositivo será apagado"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Telemóvel"</string>
     <string name="map" msgid="6068210738233985748">"Mapas"</string>
     <string name="browse" msgid="6993590095938149861">"Navegador"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contacto"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Está quase sem espaço de armazenamento"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema poderão não funcionar"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Não existe armazenamento suficiente para o sistema. Certifique-se de que tem 250 MB de espaço livre e reinicie."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index a9e0ac9..1a9eefb 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Perfil de trabalho excluído devido à ausência de um app para administrador"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"O app para administrador do perfil de trabalho não foi encontrado ou está corrompido. Consequentemente, seu perfil de trabalho e os dados relacionados foram excluídos. Entre em contato com seu administrador para receber assistência."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Seu perfil de trabalho não está mais disponível neste dispositivo"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Muitas tentativas de senha"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"O dispositivo é gerenciado"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Sua organização gerencia este dispositivo e pode monitorar o tráfego de rede. Toque para ver detalhes."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Seu dispositivo será limpo"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefone"</string>
     <string name="map" msgid="6068210738233985748">"Mapas"</string>
     <string name="browse" msgid="6993590095938149861">"Navegador"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contato"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Pouco espaço de armazenamento"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema podem não funcionar"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 67f17b25..298434c 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -174,6 +174,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Profilul de serviciu a fost șters, deoarece aplicația de administrare lipsește"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplicația de administrare a profilului de serviciu lipsește sau este deteriorată. Prin urmare, profilul de serviciu și datele asociate au fost șterse. Pentru asistență, contactați administratorul."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Profilul de serviciu nu mai este disponibil pe acest dispozitiv"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Prea multe încercări de introducere a parolei"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Dispozitivul este gestionat"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Organizația dvs. gestionează acest dispozitiv și poate monitoriza traficul în rețea. Atingeți pentru mai multe detalii."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Datele de pe dispozitiv vor fi șterse"</string>
@@ -1000,10 +1001,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefon"</string>
     <string name="map" msgid="6068210738233985748">"Hărți"</string>
     <string name="browse" msgid="6993590095938149861">"Browser"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Persoană de contact"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spațiul de stocare aproape ocupat"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Este posibil ca unele funcții de sistem să nu funcționeze"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Spațiu de stocare insuficient pentru sistem. Asigurați-vă că aveți 250 MB de spațiu liber și reporniți."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 70631d0..643978e 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -176,6 +176,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Рабочий профиль удален, поскольку отсутствует приложение для администрирования"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Приложение для администрирования рабочего профиля отсутствует или повреждено. Из-за этого рабочий профиль и связанные с ним данные были удалены. Если у вас возникли вопросы, обратитесь к администратору."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Ваш рабочий профиль больше не доступен на этом устройстве"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Это управляемое устройство"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Ваша организация управляет этим устройством и может отслеживать сетевой трафик. Подробнее…"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Все данные с устройства будут удалены"</string>
@@ -1020,10 +1022,8 @@
     <string name="dial" msgid="4204975095406423102">"Телефон"</string>
     <string name="map" msgid="6068210738233985748">"Карты"</string>
     <string name="browse" msgid="6993590095938149861">"Браузер"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Контакт"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Недостаточно памяти"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некоторые функции могут не работать"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Недостаточно свободного места для системы. Освободите не менее 250 МБ дискового пространства и перезапустите устройство."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 11c42dc..e8b963c 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"පරිපාලක යෙදුමක් නොමැති වීමෙන් කාර්යාල පැතිකඩ මකා දමන ලදි"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"කාර්යාල පැතිකඩ පාලක යෙදුම නොමැති හෝ දූෂණය වී ඇත. ප්‍රතිඵලයක් ලෙස ඔබගේ කාර්යාල පැතිකඩ සහ අදාළ දත්ත මකා දමා ඇත. සහය සඳහා ඔබගේ පරිපාලකයා සම්බන්ධ කර ගන්න."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ඔබේ කාර්යාල පැතිකඩ මෙම උපාංගය මත තවදුරටත් ලබා ගැනීමට නොහැකිය"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"මුරපද උත්සාහ කිරීම් ඉතා වැඩි ගණනකි"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"උපාංගය කළමනාකරණය කෙරේ"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"ඔබගේ ආයතනය මෙම උපාංගය කළමනාකරණය කරන අතර එය ජාල තදබදය නිරීක්ෂණය කළ හැක. විස්තර සඳහා තට්ටු කරන්න."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"ඔබගේ උපාංගය මකා දැමෙනු ඇත"</string>
@@ -982,10 +983,8 @@
     <string name="dial" msgid="4204975095406423102">"දුරකථනය"</string>
     <string name="map" msgid="6068210738233985748">"සිතියම්"</string>
     <string name="browse" msgid="6993590095938149861">"බ්‍රවුසරය"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"සම්බන්ධතා"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"ආචයනය ඉඩ ප්‍රමාණය අඩු වී ඇත"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"සමහර පද්ධති කාර්යයන් ක්‍රියා නොකරනු ඇත"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"පද්ධතිය සඳහා ප්‍රමාණවත් ඉඩ නොමැත. ඔබට 250MB නිදහස් ඉඩක් තිබෙන ඔබට තිබෙන බව සහතික කරගෙන නැවත උත්සාහ කරන්න."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 1eb24ac..36ad8b9 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -176,6 +176,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Pracovný profil bol odstránený z dôvodu chýbajúcej aplikácie na správu"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplikácia na správu pracovného profilu buď chýba, alebo je poškodená. Z toho dôvodu bol odstránený pracovný profil aj k nemu priradené dáta. Ak potrebujete pomoc, kontaktujte svojho správcu."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Váš pracovný profil už v tomto zariadení nie je k dispozícii"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Zariadenie je spravované"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Vaša organizácia spravuje toto zariadenie a môže sledovať sieťovú premávku. Klepnutím zobrazíte podrobnosti."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Vaše zariadenie bude vymazané"</string>
@@ -1020,10 +1022,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefón"</string>
     <string name="map" msgid="6068210738233985748">"Mapy"</string>
     <string name="browse" msgid="6993590095938149861">"Prehliadač"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nedostatok ukladacieho priestoru"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektoré systémové funkcie nemusia fungovať"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"V úložisku nie je dostatok voľného miesta pre systém. Zaistite, aby ste mali 250 MB voľného miesta a zariadenie reštartujte."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 4396cd3..7ad6bc2 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -176,6 +176,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Delovni profil izbrisan zaradi manjkajoče skrbniške aplikacije"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Skrbniška aplikacija delovnega profila manjka ali pa je poškodovana, zaradi česar je bil delovni profil s povezanimi podatki izbrisan. Za pomoč se obrnite na skrbnika."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Vaš delovni profil ni več na voljo v tej napravi"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Preveč poskusov vnosa gesla"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Naprava je upravljana"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Vaša organizacija upravlja to napravo in lahko nadzira omrežni promet. Dotaknite se za podrobnosti."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Podatki v napravi bodo izbrisani"</string>
@@ -1020,10 +1021,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefon"</string>
     <string name="map" msgid="6068210738233985748">"Zemljevidi"</string>
     <string name="browse" msgid="6993590095938149861">"Brskalnik"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Stik"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Prostor za shranjevanje bo pošel"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nekatere sistemske funkcije morda ne delujejo"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"V shrambi ni dovolj prostora za sistem. Sprostite 250 MB prostora in znova zaženite napravo."</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index d62f95f..7f0a693 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Profili i punës u fshi për shkak të mungesës së aplikacionit të administratorit"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplikacioni i administratorit të profilit të punës mungon ose është dëmtuar. Si rezultat i kësaj, profili yt i punës dhe të dhënat përkatëse janë fshirë. Kontakto me administratorin për ndihmë."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Profili yt i punës nuk është më i disponueshëm në këtë pajisje"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Pajisja është e menaxhuar"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Organizata jote e menaxhon këtë pajisje dhe mund të monitorojë trafikun e rrjetit. Trokit për detaje."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Pajisja do të spastrohet"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefoni"</string>
     <string name="map" msgid="6068210738233985748">"Hartat"</string>
     <string name="browse" msgid="6993590095938149861">"Shfletuesi"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontakti"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Hapësira ruajtëse po mbaron"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Disa funksione të sistemit mund të mos punojnë"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nuk ka hapësirë të mjaftueshme ruajtjeje për sistemin. Sigurohu që të kesh 250 MB hapësirë të lirë dhe pastaj të rifillosh."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 37552af..1008215 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -174,6 +174,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Профил за Work је избрисан јер недостаје апликација за администраторе"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Апликација за администраторе на профилу за Work недостаје или је оштећена. Због тога су профил за Work и повезани подаци избрисани. Обратите се администратору за помоћ."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Профил за Work више није доступан на овом уређају"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Уређајем се управља"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Организација управља овим уређајем и може да надгледа мрежни саобраћај. Додирните за детаље."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Уређај ће бити обрисан"</string>
@@ -1000,10 +1002,8 @@
     <string name="dial" msgid="4204975095406423102">"Позови"</string>
     <string name="map" msgid="6068210738233985748">"Мапе"</string>
     <string name="browse" msgid="6993590095938149861">"Прегледач"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Контакт"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Меморијски простор је на измаку"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Неке системске функције можда не функционишу"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Нема довољно меморијског простора за систем. Уверите се да имате 250 MB слободног простора и поново покрените."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 64d8cba..44a66f8 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Jobbprofilen har raderats eftersom det saknas en administratörsapp"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Administratörsappen för jobbprofilen saknas eller är skadad. Det innebär att jobbprofilen och all relaterad data har raderats. Kontakta administratören om du vill ha hjälp."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Jobbprofilen är inte längre tillgänglig på enheten"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"För många försök med lösenord"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Enheten hanteras"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Organisationen hanterar den här enheten och kan övervaka nätverkstrafiken. Tryck om du vill veta mer."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Enheten kommer att rensas"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefon"</string>
     <string name="map" msgid="6068210738233985748">"Kartor"</string>
     <string name="browse" msgid="6993590095938149861">"Webbläsare"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"Sms"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lagringsutrymmet börjar ta slut"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Det kan hända att vissa systemfunktioner inte fungerar"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Det finns inte tillräckligt med utrymme för systemet. Kontrollera att du har ett lagringsutrymme på minst 250 MB och starta om."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index c3ee444..9a78fd4 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -170,6 +170,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Wasifu wa kazini umefutwa kutokana na kupotea kwa programu ya msimamizi"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Programu ya msimamizi wa wasifu wa kazini imepotea au ina hitilafu. Kwa sababu hiyo, wasifu wako wa kazini na data husika imefutwa. Wasiliana na msimamizi wako kwa usaidizi."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Wasifu wako wa kazini haupatikani tena kwenye kifaa hiki"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Umejaribu kuweka nenosiri mara nyingi mno"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Kifaa kinadhibitiwa"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Shirika lako linadhibiti kifaa hiki na huenda likafuatilia shughuli kwenye mtandao. Gonga ili upate maelezo zaidi."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Data iliyomo kwenye kifaa chako itafutwa"</string>
@@ -978,10 +979,8 @@
     <string name="dial" msgid="4204975095406423102">"Simu"</string>
     <string name="map" msgid="6068210738233985748">"Ramani"</string>
     <string name="browse" msgid="6993590095938149861">"Kivinjari"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Anwani"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nafasi ya kuhifadhi inakaribia kujaa"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Baadhi ya vipengee vya mfumo huenda visifanye kazi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Hifadhi haitoshi kwa ajili ya mfumo. Hakikisha una MB 250 za nafasi ya hifadhi isiyotumika na uanzishe upya."</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index cd2a566..1cd5cd0 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"நிர்வாகிப் பயன்பாடு இல்லாததால், பணி விவரம் நீக்கப்பட்டது"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"பணி விவர நிர்வாகிப் பயன்பாடு இல்லை அல்லது அது சிதைந்துள்ளது. இதன் விளைவாக, உங்கள் பணி விவரமும் அதனுடன் தொடர்புடைய தரவும் நீக்கப்பட்டன. உதவிக்கு, நிர்வாகியைத் தொடர்புகொள்ளவும்."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"இந்தச் சாதனத்தில் இனி பணி விவரம் கிடைக்காது"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"சாதனம் நிர்வகிக்கப்படுகிறது"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"உங்கள் நிறுவனம் இந்தச் சாதனத்தை நிர்வகிக்கும், அத்துடன் அது நெட்வொர்க் ட்ராஃபிக்கைக் கண்காணிக்கலாம். விவரங்களுக்கு, தட்டவும்."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"சாதனத் தரவு அழிக்கப்படும்"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"ஃபோன்"</string>
     <string name="map" msgid="6068210738233985748">"வரைபடம்"</string>
     <string name="browse" msgid="6993590095938149861">"உலாவி"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"தொடர்பு"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"சேமிப்பிடம் குறைகிறது"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"சில அமைப்பு செயல்பாடுகள் வேலை செய்யாமல் போகலாம்"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"முறைமையில் போதுமான சேமிப்பகம் இல்லை. 250மெ.பை. அளவு காலி இடவசதி இருப்பதை உறுதிசெய்து மீண்டும் தொடங்கவும்."</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 90981e7..9c1a529 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"నిర్వాహక యాప్ లేనందున కార్యాలయ ప్రొఫైల్ తొలగించబడింది"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"కార్యాలయ ప్రొఫైల్ నిర్వాహక యాప్ లేదు లేదా పాడైంది. తత్ఫలితంగా, మీ కార్యాలయ ప్రొఫైల్ మరియు సంబంధిత డేటా తొలగించబడ్డాయి. సహాయం కోసం మీ నిర్వాహకులను సంప్రదించండి."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ఈ పరికరంలో మీ కార్యాలయ ప్రొఫైల్ ఇప్పుడు అందుబాటులో లేదు"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"పరికరం నిర్వహించబడింది"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"మీ సంస్థ ఈ పరికరాన్ని నిర్వహిస్తుంది మరియు నెట్‌వర్క్ ట్రాఫిక్‌ని పర్యవేక్షించవచ్చు. వివరాల కోసం నొక్కండి."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"మీ పరికరంలోని డేటా తొలగించబడుతుంది"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"ఫోన్"</string>
     <string name="map" msgid="6068210738233985748">"మ్యాప్స్"</string>
     <string name="browse" msgid="6993590095938149861">"బ్రౌజర్"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"పరిచయం"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"నిల్వ ఖాళీ అయిపోతోంది"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"కొన్ని సిస్టమ్ కార్యాచరణలు పని చేయకపోవచ్చు"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"సిస్టమ్ కోసం తగినంత నిల్వ లేదు. మీకు 250MB ఖాళీ స్థలం ఉందని నిర్ధారించుకుని, పునఃప్రారంభించండి."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 310bd95..900f950 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"ลบโปรไฟล์งานแล้วเนื่องจากไม่มีแอปผู้ดูแลระบบ"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"แอปผู้ดูแลระบบโปรไฟล์งานไม่มีอยู่หรือเสียหาย ระบบจึงทำการลบโปรไฟล์งานและข้อมูลที่เกี่ยวข้องของคุณออก โปรดติดต่อผู้ดูแลระบบเพื่อรับความช่วยเหลือ"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"โปรไฟล์งานของคุณไม่สามารถใช้ในอุปกรณ์นี้อีกต่อไป"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"อุปกรณ์มีการจัดการ"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"องค์กรของคุณจัดการอุปกรณ์นี้และอาจตรวจสอบการจราจรของข้อมูลในเครือข่าย แตะเพื่อดูรายละเอียด"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"ระบบจะลบข้อมูลในอุปกรณ์ของคุณ"</string>
@@ -666,7 +668,7 @@
     <string name="imProtocolYahoo" msgid="8271439408469021273">"Yahoo"</string>
     <string name="imProtocolSkype" msgid="9019296744622832951">"Skype"</string>
     <string name="imProtocolQq" msgid="8887484379494111884">"QQ"</string>
-    <string name="imProtocolGoogleTalk" msgid="493902321140277304">"แฮงเอาท์"</string>
+    <string name="imProtocolGoogleTalk" msgid="493902321140277304">" Hangouts"</string>
     <string name="imProtocolIcq" msgid="1574870433606517315">"ICQ"</string>
     <string name="imProtocolJabber" msgid="2279917630875771722">"Jabber"</string>
     <string name="imProtocolNetMeeting" msgid="8287625655986827971">"NetMeeting"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"โทรศัพท์"</string>
     <string name="map" msgid="6068210738233985748">"Maps"</string>
     <string name="browse" msgid="6993590095938149861">"เบราว์เซอร์"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"ติดต่อ"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"พื้นที่จัดเก็บเหลือน้อย"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"บางฟังก์ชันระบบอาจไม่ทำงาน"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"พื้นที่เก็บข้อมูลไม่เพียงพอสำหรับระบบ โปรดตรวจสอบว่าคุณมีพื้นที่ว่าง 250 MB แล้วรีสตาร์ท"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 50d09a0..267f2ed 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Na-delete ang profile sa trabaho dahil wala itong admin app"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Nawawala o nasira ang admin app ng profile sa trabaho. Dahil dito, na-delete ang profile mo sa trabaho at nauugnay na data. Makipag-ugnayan sa iyong admin para sa tulong."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Hindi na available sa device na ito ang iyong profile sa trabaho"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Pinamamahalaan ang device"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Pinamamahalaan ng iyong organisasyon ang device na ito, at maaari nitong subaybayan ang trapiko sa network. I-tap para sa mga detalye."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Buburahin ang iyong device"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Telepono"</string>
     <string name="map" msgid="6068210738233985748">"Mga Mapa"</string>
     <string name="browse" msgid="6993590095938149861">"Browser"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nauubusan na ang puwang ng storage"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Maaaring hindi gumana nang tama ang ilang paggana ng system"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Walang sapat na storage para sa system. Tiyaking mayroon kang 250MB na libreng espasyo at i-restart."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 9bead0c..3d592fe 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Eksik yönetici uygulaması nedeniyle iş profili silindi"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"İş profili yönetici uygulaması eksik ya da bozuk. Bunun sonucunda iş profiliniz ve ilgili veriler silindi. Yardım almak için yöneticiniz ile iletişim kurun."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"İş profiliniz arık bu cihazda kullanılamıyor"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Cihaz yönetiliyor"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Kuruluşunuz bu cihazı yönetmekte olup ağ trafiğini izleyebilir. Ayrıntılar için dokunun."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Cihazınız silinecek"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefon"</string>
     <string name="map" msgid="6068210738233985748">"Haritalar"</string>
     <string name="browse" msgid="6993590095938149861">"Tarayıcı"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kişi"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Depolama alanı bitiyor"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bazı sistem işlevleri çalışmayabilir"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistem için yeterli depolama alanı yok. 250 MB boş alanınızın bulunduğundan emin olun ve yeniden başlatın."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index cb2f5b4..a00f488 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -176,6 +176,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Робочий профіль видалено через відсутність додатка адміністратора"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Додаток адміністратора в робочому профілі відсутній або пошкоджений. У результаті ваш робочий профіль і пов’язані з ним дані видалено. Зверніться до свого адміністратора по допомогу."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Робочий профіль більше не доступний на цьому пристрої"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Забагато спроб ввести пароль"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Пристрій контролюється"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Адміністратор вашої організації контролює цей пристрій і відстежує мережевий трафік. Торкніться, щоб дізнатися більше."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"З вашого пристрою буде стерто всі дані"</string>
@@ -1020,10 +1021,8 @@
     <string name="dial" msgid="4204975095406423102">"Телефонувати"</string>
     <string name="map" msgid="6068210738233985748">"Карти"</string>
     <string name="browse" msgid="6993590095938149861">"Веб-переглядач"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Контакт"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Закінчується пам’ять"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Деякі системні функції можуть не працювати"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Недостатньо місця для системи. Переконайтесь, що на пристрої є 250 МБ вільного місця, і повторіть спробу."</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index ab69fc3..4e528bd 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"گمشدہ منتظم ایپ کی وجہ سے دفتری پروفائل حذف کر دیا گیا"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"دفتری پروفائل کی منتظم ایپ یا تو غائب ہے یا خراب ہے۔ اس کی وجہ سے، آپ کا دفتری پروفائل اور متعلقہ ڈیٹا حذف کر دیے گئے ہیں۔ مدد کیلئے اپنے منتظم سے رابطہ کریں۔"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"آپ کا دفتری پروفائل اس آلہ پر مزید دستیاب نہیں ہے"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"آلہ زیر انتظام ہے"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"آپ کی تنظیم اس آلے کا نظم کرتی ہے اور وہ نیٹ ورک ٹریفک کی نگرانی کر سکتی ہے۔ تفاصیل کیلئے تھپتھپائیں۔"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"آپ کا آلہ صاف کر دیا جائے گا"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"فون کریں"</string>
     <string name="map" msgid="6068210738233985748">"Maps"</string>
     <string name="browse" msgid="6993590095938149861">"براؤزر"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"رابطہ"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"اسٹوریج کی جگہ ختم ہو رہی ہے"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"ممکن ہے سسٹم کے کچھ فنکشنز کام نہ کریں"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"‏سسٹم کیلئے کافی اسٹوریج نہیں ہے۔ اس بات کو یقینی بنائیں کہ آپ کے پاس 250MB خالی جگہ ہے اور دوبارہ شروع کریں۔"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index f402e26..a269630 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Administrator ilovasi yo‘qligi sababli ishchi profil o‘chirib tashlandi"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Ishchi profilning administrator ilovasi yo‘q yoki buzilgan. Shuning uchun, ishchi profilingiz va unga aloqador ma’lumotlar o‘chirib tashlandi. Yordam olish uchun administratoringizga murojaat qiling."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Bu qurilmada endi ishchi profilingiz mavjud emas"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Parol ko‘p marta xato kiritildi"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Bu – boshqariladigan qurilma"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Tashkilotingiz bu qurilmani boshqaradi va tarmoq trafigini nazorat qilishi mumkin. Tafsilotlar uchun bosing."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Qurilmangizdagi ma’lumotlar o‘chirib tashlanadi"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Telefon"</string>
     <string name="map" msgid="6068210738233985748">"Xaritalar"</string>
     <string name="browse" msgid="6993590095938149861">"Brauzer"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Xotirada bo‘sh joy tugamoqda"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Ba‘zi tizim funksiyalari ishlamasligi mumkin"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Tizim uchun xotirada joy yetarli emas. Avval 250 megabayt joy bo‘shatib, keyin qurilmani o‘chirib yoqing."</string>
@@ -1007,9 +1006,9 @@
     <string name="whichEditApplication" msgid="144727838241402655">"Tahrirlash…"</string>
     <string name="whichEditApplicationNamed" msgid="1775815530156447790">"“%1$s” yordamida tahrirlash"</string>
     <string name="whichEditApplicationLabel" msgid="7183524181625290300">"Tahrirlash"</string>
-    <string name="whichSendApplication" msgid="6902512414057341668">"Baham ko‘rish"</string>
+    <string name="whichSendApplication" msgid="6902512414057341668">"Ulashish"</string>
     <string name="whichSendApplicationNamed" msgid="2799370240005424391">"“%1$s” orqali ulashish"</string>
-    <string name="whichSendApplicationLabel" msgid="4579076294675975354">"Baham ko‘rish"</string>
+    <string name="whichSendApplicationLabel" msgid="4579076294675975354">"Ulashish"</string>
     <string name="whichSendToApplication" msgid="8272422260066642057">"Ilovani tanlang"</string>
     <string name="whichSendToApplicationNamed" msgid="7768387871529295325">"%1$s orqali yuborish"</string>
     <string name="whichSendToApplicationLabel" msgid="8878962419005813500">"Yuborish"</string>
@@ -1417,7 +1416,7 @@
     <string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 barmoq izi:"</string>
     <string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Barchasini ko‘rish"</string>
     <string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Harakat turini tanlang"</string>
-    <string name="share_action_provider_share_with" msgid="5247684435979149216">"Baham ko‘rish"</string>
+    <string name="share_action_provider_share_with" msgid="5247684435979149216">"Ulashish"</string>
     <string name="sending" msgid="3245653681008218030">"Jo‘natilmoqda…"</string>
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Brauzer ishga tushirilsinmi?"</string>
     <string name="SetupCallDefault" msgid="5834948469253758575">"Qo‘ng‘iroqni qabul qilasizmi?"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 9a057cf..09df823 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Đã xóa hồ sơ công việc do thiếu ứng dụng quản trị"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Ứng dụng quản trị hồ sơ công việc bị thiếu hoặc hỏng. Do vậy, hồ sơ công việc của bạn và dữ liệu liên quan đã bị xóa. Hãy liên hệ với quản trị viên của bạn để được trợ giúp."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Hồ sơ công việc của bạn không có sẵn trên thiết bị này nữa"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Thiết bị được quản lý"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Tổ chức của bạn sẽ quản lý thiết bị này và có thể theo dõi lưu lượng truy cập mạng. Nhấn để biết chi tiết."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Thiết bị của bạn sẽ bị xóa"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"Điện thoại"</string>
     <string name="map" msgid="6068210738233985748">"Bản đồ"</string>
     <string name="browse" msgid="6993590095938149861">"Trình duyệt"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Liên hệ"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Sắp hết dung lượng lưu trữ"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Một số chức năng hệ thống có thể không hoạt động"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Không đủ bộ nhớ cho hệ thống. Đảm bảo bạn có 250 MB dung lượng trống và khởi động lại."</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 962b05f..b3c1af5 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"由于缺少管理应用,工作资料已被删除"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"工作资料管理应用缺失或损坏,因此系统已删除您的工作资料及相关数据。如需帮助,请与您的管理员联系。"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"您的工作资料已不在此设备上"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"设备为受管理设备"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"贵单位会管理该设备,且可能会监控网络流量。点按即可了解详情。"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"系统将清空您的设备"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"电话"</string>
     <string name="map" msgid="6068210738233985748">"地图"</string>
     <string name="browse" msgid="6993590095938149861">"浏览器"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"短信"</string>
+    <string name="add_contact" msgid="7990645816259405444">"联系人"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"存储空间不足"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"某些系统功能可能无法正常使用"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"系统存储空间不足。请确保您有250MB的可用空间,然后重新启动。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 4b5a4ec..04a62a6 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"由於沒有管理員應用程式,工作設定檔已刪除"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"工作設定檔管理員應用程式已遺失或損毀。因此,您的工作設定檔和相關資料已刪除。請聯絡您的管理員以取得協助。"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"您的工作設定檔無法再在此裝置上使用"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"裝置已受管理"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"您的機構會管理此裝置,並可能會監控網絡流量。輕按即可瞭解詳情。"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"您的裝置將被清除"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"撥打電話"</string>
     <string name="map" msgid="6068210738233985748">"地圖"</string>
     <string name="browse" msgid="6993590095938149861">"瀏覽器"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"短訊"</string>
+    <string name="add_contact" msgid="7990645816259405444">"聯絡人"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"儲存空間即將用盡"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"部分系統功能可能無法運作"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"系統儲存空間不足。請確認裝置有 250 MB 的可用空間,然後重新啟動。"</string>
@@ -1615,7 +1615,7 @@
     </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"稍後再試"</string>
     <string name="immersive_cling_title" msgid="8394201622932303336">"開啟全螢幕"</string>
-    <string name="immersive_cling_description" msgid="3482371193207536040">"由上往下刷退出。"</string>
+    <string name="immersive_cling_description" msgid="3482371193207536040">"由頂部向下快速滑動即可退出。"</string>
     <string name="immersive_cling_positive" msgid="5016839404568297683">"知道了"</string>
     <string name="done_label" msgid="2093726099505892398">"完成"</string>
     <string name="hour_picker_description" msgid="6698199186859736512">"小時環形滑桿"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 5166b87..0e9827a 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -172,6 +172,8 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Work 設定檔因管理員應用程式遺失而遭到刪除"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Work 設定檔管理員應用程式遺失或已毀損,因此系統刪除了你的 Work 設定檔和相關資料。如需協助,請與你的管理員聯絡。"</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"你的 Work 設定檔已不在這個裝置上"</string>
+    <!-- no translation found for work_profile_deleted_reason_maximum_password_failure (8986903510053359694) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="6399790108123704477">"裝置受到管理"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"貴機構會管理這個裝置,且可能監控網路流量。輕觸即可瞭解詳情。"</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"你的裝置資料將遭到清除"</string>
@@ -980,10 +982,8 @@
     <string name="dial" msgid="4204975095406423102">"電話"</string>
     <string name="map" msgid="6068210738233985748">"地圖"</string>
     <string name="browse" msgid="6993590095938149861">"瀏覽器"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"簡訊"</string>
+    <string name="add_contact" msgid="7990645816259405444">"聯絡人"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"儲存空間即將用盡"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"部分系統功能可能無法運作"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"系統儲存空間不足。請確定你已釋出 250MB 的可用空間,然後重新啟動。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index b7a6ae4..f341da1 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -172,6 +172,7 @@
     <string name="work_profile_deleted_description" msgid="1100529432509639864">"Iphrofayela yomsebenzi isuswe ngenxa yohlelo lokusebenza olungekho lomlawuli"</string>
     <string name="work_profile_deleted_details" msgid="6307630639269092360">"Uhlelo lokusebenza lokulawula lephrofayela yomsebenzi kungenzeka alukho noma lonakele. Njengomphumela, iphrofayela yakho yomsebenzi nedatha ehlobene isusiwe. Xhumana nomlawuli wakho ukuze uthole usizo."</string>
     <string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Iphrofayela yakho yomsebenzi ayisatholakali kule divayisi"</string>
+    <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Imizamo yamaphasiwedi eminingi kakhulu"</string>
     <string name="network_logging_notification_title" msgid="6399790108123704477">"Idivayisi iphethwe"</string>
     <string name="network_logging_notification_text" msgid="7930089249949354026">"Inhlangano yakho iphethe le divayisi futhi kungenzeka ingaqaphi ithrafikhi yenethiwekhi. Thephela imininingwane."</string>
     <string name="factory_reset_warning" msgid="5423253125642394387">"Idivayisi yakho izosulwa"</string>
@@ -980,10 +981,8 @@
     <string name="dial" msgid="4204975095406423102">"Ifoni"</string>
     <string name="map" msgid="6068210738233985748">"Amamephu"</string>
     <string name="browse" msgid="6993590095938149861">"Isiphequluli"</string>
-    <!-- no translation found for sms (8250353543787396737) -->
-    <skip />
-    <!-- no translation found for add_contact (7990645816259405444) -->
-    <skip />
+    <string name="sms" msgid="8250353543787396737">"I-SMS"</string>
+    <string name="add_contact" msgid="7990645816259405444">"Oxhumana naye"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Isikhala sokulondoloza siyaphela"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Eminye imisebenzi yohlelo ingahle ingasebenzi"</string>
     <string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Akusona isitoreji esanele sesistimu. Qiniseka ukuthi unesikhala esikhululekile esingu-250MB uphinde uqalise kabusha."</string>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b3d0053..14069e7 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -617,4 +617,10 @@
     -->
     <dimen name="autofill_save_icon_max_size">300dp</dimen>
 
+    <!-- Size of a slice shortcut view -->
+    <dimen name="slice_shortcut_size">56dp</dimen>
+    <!-- Size of action icons in a slice -->
+    <dimen name="slice_icon_size">24dp</dimen>
+    <!-- Standard padding used in a slice view -->
+    <dimen name="slice_padding">16dp</dimen>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 085f8dd..5189e7f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4727,4 +4727,7 @@
 
     <!-- Popup window default title to be read by a screen reader-->
     <string name="popup_window_default_title">Popup Window</string>
+
+    <!-- Format string for indicating there is more content in a slice view -->
+    <string name="slice_more_content">+ <xliff:g id="number" example="5">%1$d</xliff:g></string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cc74f17..53d09d8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3100,4 +3100,19 @@
   <java-symbol type="integer" name="config_stableDeviceDisplayWidth" />
   <java-symbol type="integer" name="config_stableDeviceDisplayHeight" />
   <java-symbol type="bool" name="config_display_no_service_when_sim_unready" />
+
+  <java-symbol type="layout" name="slice_grid" />
+  <java-symbol type="layout" name="slice_message_local" />
+  <java-symbol type="layout" name="slice_message" />
+  <java-symbol type="layout" name="slice_title" />
+  <java-symbol type="layout" name="slice_secondary_text" />
+  <java-symbol type="layout" name="slice_remote_input" />
+  <java-symbol type="layout" name="slice_small_template" />
+  <java-symbol type="id" name="remote_input_progress" />
+  <java-symbol type="id" name="remote_input_send" />
+  <java-symbol type="id" name="remote_input" />
+  <java-symbol type="dimen" name="slice_shortcut_size" />
+  <java-symbol type="dimen" name="slice_icon_size" />
+  <java-symbol type="dimen" name="slice_padding" />
+  <java-symbol type="string" name="slice_more_content" />
 </resources>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index d9bcc57d..6731536 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -168,7 +168,6 @@
                     Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
                     Settings.Global.DEVICE_DEMO_MODE,
                     Settings.Global.DEVICE_IDLE_CONSTANTS,
-                    Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH,
                     Settings.Global.BATTERY_SAVER_CONSTANTS,
                     Settings.Global.DEFAULT_SM_DP_PLUS,
                     Settings.Global.DEVICE_NAME,
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 9948f7e..902b872 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -253,12 +253,14 @@
     </privapp-permissions>
 
     <privapp-permissions package="com.android.shell">
+        <permission name="android.permission.ACCESS_LOWPAN_STATE"/>
         <permission name="android.permission.BACKUP"/>
         <permission name="android.permission.BATTERY_STATS"/>
         <permission name="android.permission.BIND_APPWIDGET"/>
         <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
         <permission name="android.permission.CHANGE_CONFIGURATION"/>
         <permission name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
+        <permission name="android.permission.CHANGE_LOWPAN_STATE"/>
         <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
         <permission name="android.permission.CLEAR_APP_CACHE"/>
         <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
@@ -282,6 +284,7 @@
         <permission name="android.permission.MOVE_PACKAGE"/>
         <permission name="android.permission.PACKAGE_USAGE_STATS" />
         <permission name="android.permission.READ_FRAME_BUFFER"/>
+        <permission name="android.permission.READ_LOWPAN_CREDENTIAL"/>
         <permission name="android.permission.REAL_GET_TASKS"/>
         <permission name="android.permission.REGISTER_CALL_PROVIDER"/>
         <permission name="android.permission.REGISTER_CONNECTION_MANAGER"/>
@@ -349,11 +352,14 @@
     </privapp-permissions>
 
     <privapp-permissions package="com.android.tv">
-        <permission name="android.permission.DVB_DEVICE" />
-        <permission name="android.permission.GLOBAL_SEARCH" />
-        <permission name="android.permission.MODIFY_PARENTAL_CONTROLS" />
-        <permission name="com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA" />
-        <permission name="com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS" />
+        <permission name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE"/>
+        <permission name="android.permission.DVB_DEVICE"/>
+        <permission name="android.permission.GLOBAL_SEARCH"/>
+        <permission name="android.permission.HDMI_CEC"/>
+        <permission name="android.permission.MODIFY_PARENTAL_CONTROLS"/>
+        <permission name="android.permission.READ_CONTENT_RATING_SYSTEMS"/>
+        <permission name="com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA"/>
+        <permission name="com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.vpndialogs">
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index b090681..1727eca 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -269,7 +269,6 @@
         setRippleActive(focused || (enabled && pressed));
 
         setBackgroundActive(hovered, hovered);
-
         return changed;
     }
 
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index 0de3f11..a675eaf 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -41,7 +41,6 @@
 
     // Pixel-based accelerations and velocities.
     private static final float WAVE_TOUCH_DOWN_ACCELERATION = 1024;
-    private static final float WAVE_TOUCH_UP_ACCELERATION = 3400;
     private static final float WAVE_OPACITY_DECAY_VELOCITY = 3;
 
     // Bounded ripple animation properties.
@@ -83,6 +82,11 @@
     /** Whether this ripple has finished its exit animation. */
     private boolean mHasFinishedExit;
 
+    /**
+     * If we have a bound, don't start from 0. Start from 60% of the max out of width and height.
+     */
+    private float mStartRadius = 0;
+
     public RippleForeground(RippleDrawable owner, Rect bounds, float startingX, float startingY,
             boolean isBounded, boolean forceSoftware) {
         super(owner, bounds, forceSoftware);
@@ -96,6 +100,8 @@
         } else {
             mBoundedRadius = 0;
         }
+        // Take 60% of the maximum of the width and height, then divided half to get the radius.
+        mStartRadius = Math.max(bounds.width(), bounds.height()) * 0.3f;
     }
 
     @Override
@@ -158,19 +164,18 @@
 
     @Override
     protected Animator createSoftwareEnter(boolean fast) {
-        final int duration = (int)
-                (1000 * Math.sqrt(mTargetRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensityScale) + 0.5);
+        final int duration = getRadiusDuration();
 
         final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
         tweenRadius.setAutoCancel(true);
         tweenRadius.setDuration(duration);
-        tweenRadius.setInterpolator(LINEAR_INTERPOLATOR);
+        tweenRadius.setInterpolator(DECELERATE_INTERPOLATOR);
         tweenRadius.setStartDelay(RIPPLE_ENTER_DELAY);
 
         final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
         tweenOrigin.setAutoCancel(true);
         tweenOrigin.setDuration(duration);
-        tweenOrigin.setInterpolator(LINEAR_INTERPOLATOR);
+        tweenOrigin.setInterpolator(DECELERATE_INTERPOLATOR);
         tweenOrigin.setStartDelay(RIPPLE_ENTER_DELAY);
 
         final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1);
@@ -192,14 +197,14 @@
         return MathUtils.lerp(mClampedStartingY - mBounds.exactCenterY(), mTargetY, mTweenY);
     }
 
-    private int getRadiusExitDuration() {
+    private int getRadiusDuration() {
         final float remainingRadius = mTargetRadius - getCurrentRadius();
-        return (int) (1000 * Math.sqrt(remainingRadius / (WAVE_TOUCH_UP_ACCELERATION
-                + WAVE_TOUCH_DOWN_ACCELERATION) * mDensityScale) + 0.5);
+        return (int) (1000 * Math.sqrt(remainingRadius / WAVE_TOUCH_DOWN_ACCELERATION *
+                mDensityScale) + 0.5);
     }
 
     private float getCurrentRadius() {
-        return MathUtils.lerp(0, mTargetRadius, mTweenRadius);
+        return MathUtils.lerp(mStartRadius, mTargetRadius, mTweenRadius);
     }
 
     private int getOpacityExitDuration() {
@@ -212,7 +217,7 @@
         final int originDuration;
         final int opacityDuration;
 
-        radiusDuration = getRadiusExitDuration();
+        radiusDuration = getRadiusDuration();
         originDuration = radiusDuration;
         opacityDuration = getOpacityExitDuration();
 
@@ -244,7 +249,7 @@
         final int originDuration;
         final int opacityDuration;
 
-        radiusDuration = getRadiusExitDuration();
+        radiusDuration = getRadiusDuration();
         originDuration = radiusDuration;
         opacityDuration = getOpacityExitDuration();
 
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index 0a38874..7cc0871 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -34,12 +34,12 @@
 
 namespace android {
 
-static SkTypeface::Style computeSkiaStyle(int weight, bool italic) {
+static Typeface::Style computeAPIStyle(int weight, bool italic) {
     // This bold detection comes from SkTypeface.h
     if (weight >= SkFontStyle::kSemiBold_Weight) {
-        return italic ? SkTypeface::kBoldItalic : SkTypeface::kBold;
+        return italic ? Typeface::kBoldItalic : Typeface::kBold;
     } else {
-        return italic ? SkTypeface::kItalic : SkTypeface::kNormal;
+        return italic ? Typeface::kItalic : Typeface::kNormal;
     }
 }
 
@@ -50,12 +50,12 @@
 }
 
 // Resolve the relative weight from the baseWeight and target style.
-static minikin::FontStyle computeRelativeStyle(int baseWeight, SkTypeface::Style relativeStyle) {
+static minikin::FontStyle computeRelativeStyle(int baseWeight, Typeface::Style relativeStyle) {
     int weight = baseWeight;
-    if ((relativeStyle & SkTypeface::kBold) != 0) {
+    if ((relativeStyle & Typeface::kBold) != 0) {
         weight += 300;
     }
-    bool italic = (relativeStyle & SkTypeface::kItalic) != 0;
+    bool italic = (relativeStyle & Typeface::kItalic) != 0;
     return computeMinikinStyle(weight, italic);
 }
 
@@ -66,13 +66,13 @@
     return src == nullptr ? gDefaultTypeface : src;
 }
 
-Typeface* Typeface::createRelative(Typeface* src, SkTypeface::Style style) {
+Typeface* Typeface::createRelative(Typeface* src, Typeface::Style style) {
     const Typeface* resolvedFace = Typeface::resolveDefault(src);
     Typeface* result = new Typeface;
     if (result != nullptr) {
         result->fFontCollection = resolvedFace->fFontCollection;
         result->fBaseWeight = resolvedFace->fBaseWeight;
-        result->fSkiaStyle = style;
+        result->fAPIStyle = style;
         result->fStyle = computeRelativeStyle(result->fBaseWeight, style);
     }
     return result;
@@ -84,7 +84,7 @@
     if (result != nullptr) {
         result->fFontCollection = resolvedFace->fFontCollection;
         result->fBaseWeight = resolvedFace->fBaseWeight;
-        result->fSkiaStyle = computeSkiaStyle(weight, italic);
+        result->fAPIStyle = computeAPIStyle(weight, italic);
         result->fStyle = computeMinikinStyle(weight, italic);
     }
     return result;
@@ -105,7 +105,7 @@
         // Do not update styles.
         // TODO: We may want to update base weight if the 'wght' is specified.
         result->fBaseWeight = resolvedFace->fBaseWeight;
-        result->fSkiaStyle = resolvedFace->fSkiaStyle;
+        result->fAPIStyle = resolvedFace->fAPIStyle;
         result->fStyle = resolvedFace->fStyle;
     }
     return result;
@@ -117,8 +117,8 @@
     if (result != nullptr) {
         result->fFontCollection = resolvedFace->fFontCollection;
         result->fBaseWeight = weight;
-        result->fSkiaStyle = resolvedFace->fSkiaStyle;
-        result->fStyle = computeRelativeStyle(weight, result->fSkiaStyle);
+        result->fAPIStyle = resolvedFace->fAPIStyle;
+        result->fStyle = computeRelativeStyle(weight, result->fAPIStyle);
     }
     return result;
 }
@@ -161,7 +161,7 @@
     }
 
     result->fBaseWeight = weight;
-    result->fSkiaStyle = computeSkiaStyle(weight, italic);
+    result->fAPIStyle = computeAPIStyle(weight, italic);
     result->fStyle = computeMinikinStyle(weight, italic);
     return result;
 }
@@ -191,7 +191,7 @@
 
     Typeface* hwTypeface = new Typeface();
     hwTypeface->fFontCollection = collection;
-    hwTypeface->fSkiaStyle = SkTypeface::kNormal;
+    hwTypeface->fAPIStyle = Typeface::kNormal;
     hwTypeface->fBaseWeight = SkFontStyle::kNormal_Weight;
     hwTypeface->fStyle = minikin::FontStyle(4 /* weight */, false /* italic */);
 
diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h
index 38c6234..d90114d 100644
--- a/libs/hwui/hwui/Typeface.h
+++ b/libs/hwui/hwui/Typeface.h
@@ -38,8 +38,14 @@
     // resolved style actually used for rendering
     minikin::FontStyle fStyle;
 
-    // style used for constructing and querying Typeface objects
-    SkTypeface::Style fSkiaStyle;
+    // style used in the API
+    enum Style : uint8_t {
+        kNormal = 0,
+        kBold   = 0x01,
+        kItalic = 0x02,
+        kBoldItalic = 0x03
+    };
+    Style fAPIStyle;
 
     static const Typeface* resolveDefault(const Typeface* src);
 
@@ -68,7 +74,7 @@
     //
     //   Typeface* black = createAbsolute(base, 900, false);  // Rendered with a weight of 900.
     static Typeface* createWithDifferentBaseWeight(Typeface* src, int baseweight);
-    static Typeface* createRelative(Typeface* src, SkTypeface::Style desiredStyle);
+    static Typeface* createRelative(Typeface* src, Style desiredStyle);
     static Typeface* createAbsolute(Typeface* base, int weight, bool italic);
 
     static Typeface* createFromTypefaceWithVariation(Typeface* src,
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 5c6078d..3b72a8b 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -72,15 +72,23 @@
             textureMatrixInv = textureMatrix;
         }
 
-        SkMatrix matrix = SkMatrix::Concat(textureMatrixInv, layerTransform);
+        SkMatrix matrix = SkMatrix::Concat(layerTransform, textureMatrixInv);
 
         SkPaint paint;
         paint.setAlpha(layer->getAlpha());
         paint.setBlendMode(layer->getMode());
         paint.setColorFilter(sk_ref_sp(layer->getColorFilter()));
-        // draw image with a shader to avoid save/restore of the matrix
-        paint.setShader(layerImage->makeShader(&matrix));
-        canvas->drawRect(SkRect::MakeWH(layerWidth, layerHeight), paint);
+
+        const bool nonIdentityMatrix = !matrix.isIdentity();
+        if (nonIdentityMatrix) {
+            canvas->save();
+            canvas->concat(matrix);
+        }
+        canvas->drawImage(layerImage.get(), 0, 0, &paint);
+        // restore the original matrix
+        if (nonIdentityMatrix) {
+            canvas->restore();
+        }
     }
 
     return layerImage;
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 439d690..1f3eaef 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -84,159 +84,159 @@
     std::unique_ptr<Typeface> bold(Typeface::createWithDifferentBaseWeight(nullptr, 700));
     EXPECT_EQ(7, bold->fStyle.getWeight());
     EXPECT_FALSE(bold->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kNormal, bold->fSkiaStyle);
+    EXPECT_EQ(Typeface::kNormal, bold->fAPIStyle);
 
     std::unique_ptr<Typeface> light(Typeface::createWithDifferentBaseWeight(nullptr, 300));
     EXPECT_EQ(3, light->fStyle.getWeight());
     EXPECT_FALSE(light->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kNormal, light->fSkiaStyle);
+    EXPECT_EQ(Typeface::kNormal, light->fAPIStyle);
 }
 
 TEST(TypefaceTest, createRelativeTest_fromRegular) {
     // In Java, Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);
-    std::unique_ptr<Typeface> normal(Typeface::createRelative(nullptr, SkTypeface::kNormal));
+    std::unique_ptr<Typeface> normal(Typeface::createRelative(nullptr, Typeface::kNormal));
     EXPECT_EQ(4, normal->fStyle.getWeight());
     EXPECT_FALSE(normal->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+    EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD);
-    std::unique_ptr<Typeface> bold(Typeface::createRelative(nullptr, SkTypeface::kBold));
+    std::unique_ptr<Typeface> bold(Typeface::createRelative(nullptr, Typeface::kBold));
     EXPECT_EQ(7, bold->fStyle.getWeight());
     EXPECT_FALSE(bold->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.DEFAULT, Typeface.ITALIC);
-    std::unique_ptr<Typeface> italic(Typeface::createRelative(nullptr, SkTypeface::kItalic));
+    std::unique_ptr<Typeface> italic(Typeface::createRelative(nullptr, Typeface::kItalic));
     EXPECT_EQ(4, italic->fStyle.getWeight());
     EXPECT_TRUE(italic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface> boldItalic(
-            Typeface::createRelative(nullptr, SkTypeface::kBoldItalic));
+            Typeface::createRelative(nullptr, Typeface::kBoldItalic));
     EXPECT_EQ(7, boldItalic->fStyle.getWeight());
     EXPECT_TRUE(boldItalic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
 TEST(TypefaceTest, createRelativeTest_BoldBase) {
     std::unique_ptr<Typeface> base(Typeface::createWithDifferentBaseWeight(nullptr, 700));
 
     // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.NORMAL);
-    std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal));
+    std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
     EXPECT_EQ(7, normal->fStyle.getWeight());
     EXPECT_FALSE(normal->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+    EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.BOLD);
-    std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold));
+    std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
     EXPECT_EQ(10, bold->fStyle.getWeight());
     EXPECT_FALSE(bold->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.ITALIC);
-    std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic));
+    std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
     EXPECT_EQ(7, italic->fStyle.getWeight());
     EXPECT_TRUE(italic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface>
-            boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic));
+            boldItalic(Typeface::createRelative(base.get(), Typeface::kBoldItalic));
     EXPECT_EQ(10, boldItalic->fStyle.getWeight());
     EXPECT_TRUE(boldItalic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
 TEST(TypefaceTest, createRelativeTest_LightBase) {
     std::unique_ptr<Typeface> base(Typeface::createWithDifferentBaseWeight(nullptr, 300));
 
     // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.NORMAL);
-    std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal));
+    std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
     EXPECT_EQ(3, normal->fStyle.getWeight());
     EXPECT_FALSE(normal->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+    EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.BOLD);
-    std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold));
+    std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
     EXPECT_EQ(6, bold->fStyle.getWeight());
     EXPECT_FALSE(bold->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.ITLIC);
-    std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic));
+    std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
     EXPECT_EQ(3, italic->fStyle.getWeight());
     EXPECT_TRUE(italic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface>
-            boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic));
+            boldItalic(Typeface::createRelative(base.get(), Typeface::kBoldItalic));
     EXPECT_EQ(6, boldItalic->fStyle.getWeight());
     EXPECT_TRUE(boldItalic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
 TEST(TypefaceTest, createRelativeTest_fromBoldStyled) {
-    std::unique_ptr<Typeface> base(Typeface::createRelative(nullptr, SkTypeface::kBold));
+    std::unique_ptr<Typeface> base(Typeface::createRelative(nullptr, Typeface::kBold));
 
     // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.NORMAL);
-    std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal));
+    std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
     EXPECT_EQ(4, normal->fStyle.getWeight());
     EXPECT_FALSE(normal->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+    EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.BOLD);
-    std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold));
+    std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
     EXPECT_EQ(7, bold->fStyle.getWeight());
     EXPECT_FALSE(bold->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.ITALIC);
-    std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic));
+    std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
     EXPECT_EQ(4, normal->fStyle.getWeight());
     EXPECT_TRUE(italic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
     // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface>
-            boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic));
+            boldItalic(Typeface::createRelative(base.get(), Typeface::kBoldItalic));
     EXPECT_EQ(7, boldItalic->fStyle.getWeight());
     EXPECT_TRUE(boldItalic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
 TEST(TypefaceTest, createRelativeTest_fromItalicStyled) {
-    std::unique_ptr<Typeface> base(Typeface::createRelative(nullptr, SkTypeface::kItalic));
+    std::unique_ptr<Typeface> base(Typeface::createRelative(nullptr, Typeface::kItalic));
 
     // In Java,
     // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.NORMAL);
-    std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal));
+    std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
     EXPECT_EQ(4, normal->fStyle.getWeight());
     EXPECT_FALSE(normal->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+    EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.BOLD);
-    std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold));
+    std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
     EXPECT_EQ(7, bold->fStyle.getWeight());
     EXPECT_FALSE(bold->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java,
     // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.ITALIC);
-    std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic));
+    std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
     EXPECT_EQ(4, italic->fStyle.getWeight());
     EXPECT_TRUE(italic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
     // Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface>
-            boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic));
+            boldItalic(Typeface::createRelative(base.get(), Typeface::kBoldItalic));
     EXPECT_EQ(7, boldItalic->fStyle.getWeight());
     EXPECT_TRUE(boldItalic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
 TEST(TypefaceTest, createRelativeTest_fromSpecifiedStyled) {
@@ -246,38 +246,38 @@
     // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif")
     //     .setWeight(700).setItalic(false).build();
     // Typeface.create(typeface, Typeface.NORMAL);
-    std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal));
+    std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
     EXPECT_EQ(4, normal->fStyle.getWeight());
     EXPECT_FALSE(normal->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+    EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
 
     // In Java,
     // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif")
     //     .setWeight(700).setItalic(false).build();
     // Typeface.create(typeface, Typeface.BOLD);
-    std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold));
+    std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
     EXPECT_EQ(7, bold->fStyle.getWeight());
     EXPECT_FALSE(bold->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java,
     // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif")
     //     .setWeight(700).setItalic(false).build();
     // Typeface.create(typeface, Typeface.ITALIC);
-    std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic));
+    std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
     EXPECT_EQ(4, italic->fStyle.getWeight());
     EXPECT_TRUE(italic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
     // Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif")
     //     .setWeight(700).setItalic(false).build();
     // Typeface.create(typeface, Typeface.BOLD_ITALIC);
     std::unique_ptr<Typeface>
-            boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic));
+            boldItalic(Typeface::createRelative(base.get(), Typeface::kBoldItalic));
     EXPECT_EQ(7, boldItalic->fStyle.getWeight());
     EXPECT_TRUE(boldItalic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 }
 
 TEST(TypefaceTest, createAbsolute) {
@@ -287,7 +287,7 @@
     std::unique_ptr<Typeface> regular(Typeface::createAbsolute(nullptr, 400, false));
     EXPECT_EQ(4, regular->fStyle.getWeight());
     EXPECT_FALSE(regular->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kNormal, regular->fSkiaStyle);
+    EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
 
     // In Java,
     // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(false)
@@ -295,7 +295,7 @@
     std::unique_ptr<Typeface> bold(Typeface::createAbsolute(nullptr, 700, false));
     EXPECT_EQ(7, bold->fStyle.getWeight());
     EXPECT_FALSE(bold->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java,
     // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(true)
@@ -303,7 +303,7 @@
     std::unique_ptr<Typeface> italic(Typeface::createAbsolute(nullptr, 400, true));
     EXPECT_EQ(4, italic->fStyle.getWeight());
     EXPECT_TRUE(italic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
     // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(true)
@@ -311,7 +311,7 @@
     std::unique_ptr<Typeface> boldItalic(Typeface::createAbsolute(nullptr, 700, true));
     EXPECT_EQ(7, boldItalic->fStyle.getWeight());
     EXPECT_TRUE(boldItalic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
 
     // In Java,
     // new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(1100).setItalic(true)
@@ -319,7 +319,7 @@
     std::unique_ptr<Typeface> over1000(Typeface::createAbsolute(nullptr, 1100, false));
     EXPECT_EQ(10, over1000->fStyle.getWeight());
     EXPECT_FALSE(over1000->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBold, over1000->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle);
 }
 
 TEST(TypefaceTest, createFromFamilies_Single) {
@@ -328,21 +328,21 @@
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular), 400, false));
     EXPECT_EQ(4, regular->fStyle.getWeight());
     EXPECT_FALSE(regular->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kNormal, regular->fSkiaStyle);
+    EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
 
     // In Java, new Typeface.Builder("Roboto-Bold.ttf").setWeight(700).setItalic(false).build();
     std::unique_ptr<Typeface> bold(
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 700, false));
     EXPECT_EQ(7, bold->fStyle.getWeight());
     EXPECT_FALSE(bold->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, new Typeface.Builder("Roboto-Italic.ttf").setWeight(400).setItalic(true).build();
     std::unique_ptr<Typeface> italic(
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoItalic), 400, true));
     EXPECT_EQ(4, italic->fStyle.getWeight());
     EXPECT_TRUE(italic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
     // new Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(700).setItalic(true).build();
@@ -350,7 +350,7 @@
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic), 700, true));
     EXPECT_EQ(7, boldItalic->fStyle.getWeight());
     EXPECT_TRUE(boldItalic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java,
     // new Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(1100).setItalic(false).build();
@@ -358,7 +358,7 @@
             Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 1100, false));
     EXPECT_EQ(10, over1000->fStyle.getWeight());
     EXPECT_FALSE(over1000->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBold, over1000->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle);
 }
 
 TEST(TypefaceTest, createFromFamilies_Single_resolveByTable) {
@@ -368,7 +368,7 @@
                     RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
     EXPECT_EQ(4, regular->fStyle.getWeight());
     EXPECT_FALSE(regular->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kNormal, regular->fSkiaStyle);
+    EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
 
     // In Java, new Typeface.Builder("Roboto-Bold.ttf").build();
     std::unique_ptr<Typeface> bold(
@@ -376,7 +376,7 @@
                     RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
     EXPECT_EQ(7, bold->fStyle.getWeight());
     EXPECT_FALSE(bold->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+    EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
 
     // In Java, new Typeface.Builder("Roboto-Italic.ttf").build();
     std::unique_ptr<Typeface> italic(
@@ -384,7 +384,7 @@
                     RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
     EXPECT_EQ(4, italic->fStyle.getWeight());
     EXPECT_TRUE(italic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 
     // In Java, new Typeface.Builder("Roboto-BoldItalic.ttf").build();
     std::unique_ptr<Typeface> boldItalic(
@@ -392,7 +392,7 @@
                     RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
     EXPECT_EQ(7, boldItalic->fStyle.getWeight());
     EXPECT_TRUE(boldItalic->fStyle.getItalic());
-    EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+    EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
 }
 
 TEST(TypefaceTest, createFromFamilies_Family) {
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index d456950..0704e35 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -12,6 +12,32 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+cc_library_shared {
+    name: "libjnigraphics",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wunused",
+        "-Wunreachable-code",
+    ],
+
+    // our source files
+    //
+    srcs: ["bitmap.cpp"],
+
+    shared_libs: [
+        "libandroid_runtime",
+    ],
+
+    arch: {
+        arm: {
+            // TODO: This is to work around b/24465209. Remove after root cause is fixed
+            ldflags: ["-Wl,--hash-style=both"],
+        },
+    },
+}
+
 // The headers module is in frameworks/native/Android.bp.
 ndk_library {
     name: "libjnigraphics",
diff --git a/native/graphics/jni/Android.mk b/native/graphics/jni/Android.mk
deleted file mode 100644
index 7a40e62..0000000
--- a/native/graphics/jni/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-BASE_PATH := $(call my-dir)
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# setup for skia optimizations
-#
-ifneq ($(ARCH_ARM_HAVE_VFP),true)
-    LOCAL_CFLAGS += -DSK_SOFTWARE_FLOAT
-endif
-
-ifeq ($(ARCH_ARM_HAVE_NEON),true)
-    LOCAL_CFLAGS += -D__ARM_HAVE_NEON
-endif
-
-# our source files
-#
-LOCAL_SRC_FILES:= \
-    bitmap.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-    libandroid_runtime \
-    libui \
-    libandroidfw
-
-LOCAL_C_INCLUDES += \
-    frameworks/base/core/jni/android/graphics
-
-LOCAL_MODULE:= libjnigraphics
-
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-
-# TODO: This is to work around b/24465209. Remove after root cause is fixed
-LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
-
-include $(BUILD_SHARED_LIBRARY)
-
diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp
index bf5cabb..ff14832 100644
--- a/native/graphics/jni/bitmap.cpp
+++ b/native/graphics/jni/bitmap.cpp
@@ -15,7 +15,7 @@
  */
 
 #include <android/bitmap.h>
-#include <Bitmap.h>
+#include <android/graphics/Bitmap.h>
 
 int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
                           AndroidBitmapInfo* info) {
@@ -56,4 +56,3 @@
     }
     return ANDROID_BITMAP_RESULT_SUCCESS;
 }
-
diff --git a/packages/CaptivePortalLogin/res/values-in/strings.xml b/packages/CaptivePortalLogin/res/values-in/strings.xml
index 7fa3a0a..f9f6481 100644
--- a/packages/CaptivePortalLogin/res/values-in/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-in/strings.xml
@@ -7,6 +7,6 @@
     <string name="action_bar_label" msgid="917235635415966620">"Masuk ke jaringan"</string>
     <string name="action_bar_title" msgid="5645564790486983117">"Login ke %1$s"</string>
     <string name="ssl_error_warning" msgid="6653188881418638872">"Jaringan yang ingin Anda masuki mengalami masalah keamanan."</string>
-    <string name="ssl_error_example" msgid="647898534624078900">"Misalnya, laman masuk mungkin bukan milik organisasi yang ditampilkan."</string>
+    <string name="ssl_error_example" msgid="647898534624078900">"Misalnya, halaman masuk mungkin bukan milik organisasi yang ditampilkan."</string>
     <string name="ssl_error_continue" msgid="6492718244923937110">"Tetap lanjutkan melalui browser"</string>
 </resources>
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
index f54b6fb..291009e 100644
--- a/packages/ExtServices/AndroidManifest.xml
+++ b/packages/ExtServices/AndroidManifest.xml
@@ -42,6 +42,15 @@
             </intent-filter>
         </service>
 
+        <service android:name=".notification.Assistant"
+                 android:label="@string/notification_assistant"
+                 android:permission="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"
+                 android:exported="true">
+            <intent-filter>
+                <action android:name="android.service.notification.NotificationAssistantService" />
+            </intent-filter>
+        </service>
+
         <library android:name="android.ext.services"/>
     </application>
 
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
index 531e517..a2e65bc 100644
--- a/packages/ExtServices/res/values/strings.xml
+++ b/packages/ExtServices/res/values/strings.xml
@@ -16,4 +16,7 @@
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name">Android Services Library</string>
+
+    <string name="notification_assistant">Notification Assistant</string>
+    <string name="prompt_block_reason">Too many dismissals:views</string>
 </resources>
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
new file mode 100644
index 0000000..f535368
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -0,0 +1,165 @@
+/**
+ * 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.ext.services.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.service.notification.NotificationListenerService.Ranking
+        .USER_SENTIMENT_NEGATIVE;
+
+import android.app.INotificationManager;
+import android.content.Context;
+import android.ext.services.R;
+import android.os.Bundle;
+import android.service.notification.Adjustment;
+import android.service.notification.NotificationAssistantService;
+import android.service.notification.NotificationStats;
+import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+/**
+ * Notification assistant that provides guidance on notification channel blocking
+ */
+public class Assistant extends NotificationAssistantService {
+    private static final String TAG = "ExtAssistant";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private static final ArrayList<Integer> DISMISS_WITH_PREJUDICE = new ArrayList<>();
+    static {
+        DISMISS_WITH_PREJUDICE.add(REASON_CANCEL);
+        DISMISS_WITH_PREJUDICE.add(REASON_LISTENER_CANCEL);
+    }
+
+    // key : impressions tracker
+    // TODO: persist across reboots
+    ArrayMap<String, ChannelImpressions> mkeyToImpressions = new ArrayMap<>();
+    // SBN key : channel id
+    ArrayMap<String, String> mLiveNotifications = new ArrayMap<>();
+
+    private Ranking mFakeRanking = null;
+
+    @Override
+    public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
+        if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey());
+        return null;
+    }
+
+    @Override
+    public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
+        if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey());
+        try {
+            Ranking ranking = getRanking(sbn.getKey(), rankingMap);
+            if (ranking != null && ranking.getChannel() != null) {
+                String key = getKey(
+                        sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId());
+                ChannelImpressions ci = mkeyToImpressions.getOrDefault(key,
+                        new ChannelImpressions());
+                if (ranking.getImportance() > IMPORTANCE_MIN && ci.shouldTriggerBlock()) {
+                    adjustNotification(createNegativeAdjustment(
+                            sbn.getPackageName(), sbn.getKey(), sbn.getUserId()));
+                }
+                mkeyToImpressions.put(key, ci);
+                mLiveNotifications.put(sbn.getKey(), ranking.getChannel().getId());
+            }
+        } catch (Throwable e) {
+            Log.e(TAG, "Error occurred processing post", e);
+        }
+    }
+
+    @Override
+    public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
+            NotificationStats stats, int reason) {
+        try {
+            String channelId = mLiveNotifications.remove(sbn.getKey());
+            String key = getKey(sbn.getPackageName(), sbn.getUserId(), channelId);
+            ChannelImpressions ci = mkeyToImpressions.getOrDefault(key, new ChannelImpressions());
+            if (stats.hasSeen()) {
+                ci.incrementViews();
+            }
+            if (DISMISS_WITH_PREJUDICE.contains(reason)
+                    && !sbn.isAppGroup()
+                    && !sbn.getNotification().isGroupChild()
+                    && !stats.hasInteracted()
+                    && stats.getDismissalSurface() != NotificationStats.DISMISSAL_AOD
+                    && stats.getDismissalSurface() != NotificationStats.DISMISSAL_PEEK
+                    && stats.getDismissalSurface() != NotificationStats.DISMISSAL_OTHER) {
+               if (DEBUG) Log.i(TAG, "increment dismissals");
+                ci.incrementDismissals();
+            } else {
+                if (DEBUG) Slog.i(TAG, "reset streak");
+                ci.resetStreak();
+            }
+            mkeyToImpressions.put(key, ci);
+        } catch (Throwable e) {
+            Slog.e(TAG, "Error occurred processing removal", e);
+        }
+    }
+
+    @Override
+    public void onNotificationSnoozedUntilContext(StatusBarNotification sbn,
+            String snoozeCriterionId) {
+    }
+
+    @Override
+    public void onListenerConnected() {
+        if (DEBUG) Log.i(TAG, "CONNECTED");
+        try {
+            for (StatusBarNotification sbn : getActiveNotifications()) {
+                onNotificationPosted(sbn);
+            }
+        } catch (Throwable e) {
+            Log.e(TAG, "Error occurred on connection", e);
+        }
+    }
+
+    private String getKey(String pkg, int userId, String channelId) {
+        return pkg + "|" + userId + "|" + channelId;
+    }
+
+    private Ranking getRanking(String key, RankingMap rankingMap) {
+        if (mFakeRanking != null) {
+            return mFakeRanking;
+        }
+        Ranking ranking = new Ranking();
+        rankingMap.getRanking(key, ranking);
+        return ranking;
+    }
+
+    private Adjustment createNegativeAdjustment(String packageName, String key, int user) {
+        if (DEBUG) Log.d(TAG, "User probably doesn't want " + key);
+        Bundle signals = new Bundle();
+        signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
+        return new Adjustment(packageName, key,  signals,
+                getContext().getString(R.string.prompt_block_reason), user);
+    }
+
+    // for testing
+    protected void setFakeRanking(Ranking ranking) {
+        mFakeRanking = ranking;
+    }
+
+    protected void setNoMan(INotificationManager noMan) {
+        mNoMan = noMan;
+    }
+
+    protected void setContext(Context context) {
+        mSystemContext = context;
+    }
+}
\ No newline at end of file
diff --git a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
new file mode 100644
index 0000000..30567cc
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
@@ -0,0 +1,137 @@
+/**
+ * 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.ext.services.notification;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+public final class ChannelImpressions implements Parcelable {
+    private static final String TAG = "ExtAssistant.CI";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    static final double DISMISS_TO_VIEW_RATIO_LIMIT = .8;
+    static final int STREAK_LIMIT = 2;
+
+    private int mDismissals = 0;
+    private int mViews = 0;
+    private int mStreak = 0;
+
+    public ChannelImpressions() {
+    }
+
+    public ChannelImpressions(int dismissals, int views) {
+        mDismissals = dismissals;
+        mViews = views;
+    }
+
+    protected ChannelImpressions(Parcel in) {
+        mDismissals = in.readInt();
+        mViews = in.readInt();
+        mStreak = in.readInt();
+    }
+
+    public int getStreak() {
+        return mStreak;
+    }
+
+    public int getDismissals() {
+        return mDismissals;
+    }
+
+    public int getViews() {
+        return mViews;
+    }
+
+    public void incrementDismissals() {
+        mDismissals++;
+        mStreak++;
+    }
+
+    public void incrementViews() {
+        mViews++;
+    }
+
+    public void resetStreak() {
+        mStreak = 0;
+    }
+
+    public boolean shouldTriggerBlock() {
+        if (getViews() == 0) {
+            return false;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "should trigger? " + getDismissals() + " " + getViews() + " " + getStreak());
+        }
+        return ((double) getDismissals() / getViews()) > DISMISS_TO_VIEW_RATIO_LIMIT
+                && getStreak() > STREAK_LIMIT;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mDismissals);
+        dest.writeInt(mViews);
+        dest.writeInt(mStreak);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<ChannelImpressions> CREATOR = new Creator<ChannelImpressions>() {
+        @Override
+        public ChannelImpressions createFromParcel(Parcel in) {
+            return new ChannelImpressions(in);
+        }
+
+        @Override
+        public ChannelImpressions[] newArray(int size) {
+            return new ChannelImpressions[size];
+        }
+    };
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        ChannelImpressions that = (ChannelImpressions) o;
+
+        if (mDismissals != that.mDismissals) return false;
+        if (mViews != that.mViews) return false;
+        return mStreak == that.mStreak;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mDismissals;
+        result = 31 * result + mViews;
+        result = 31 * result + mStreak;
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("ChannelImpressions{");
+        sb.append("mDismissals=").append(mDismissals);
+        sb.append(", mViews=").append(mViews);
+        sb.append(", mStreak=").append(mStreak);
+        sb.append('}');
+        return sb.toString();
+    }
+}
diff --git a/packages/ExtServices/tests/Android.mk b/packages/ExtServices/tests/Android.mk
index cb3c352..92afbeb 100644
--- a/packages/ExtServices/tests/Android.mk
+++ b/packages/ExtServices/tests/Android.mk
@@ -12,7 +12,8 @@
     mockito-target-minus-junit4 \
     espresso-core \
     truth-prebuilt \
-    legacy-android-test
+    legacy-android-test \
+    testables
 
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
new file mode 100644
index 0000000..4e5e9f9
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -0,0 +1,268 @@
+/**
+ * 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.ext.services.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+
+import static org.mockito.ArgumentMatchers.any;
+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;
+
+import android.app.INotificationManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.content.Intent;
+import android.ext.services.R;
+import android.os.UserHandle;
+import android.service.notification.Adjustment;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.NotificationStats;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.test.ServiceTestCase;
+import android.testing.TestableContext;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class AssistantTest extends ServiceTestCase<Assistant> {
+
+    private static final String PKG1 = "pkg1";
+    private static final int UID1 = 1;
+    private static final NotificationChannel P1C1 =
+            new NotificationChannel("one", "", IMPORTANCE_LOW);
+    private static final NotificationChannel P1C2 =
+            new NotificationChannel("p1c2", "", IMPORTANCE_DEFAULT);
+    private static final NotificationChannel P1C3 =
+            new NotificationChannel("p1c3", "", IMPORTANCE_MIN);
+    private static final String PKG2 = "pkg2";
+
+    private static final int UID2 = 2;
+    private static final NotificationChannel P2C1 =
+            new NotificationChannel("one", "", IMPORTANCE_LOW);
+
+    @Mock INotificationManager mNoMan;
+
+    Assistant mAssistant;
+
+    @Rule
+    public final TestableContext mContext =
+            new TestableContext(InstrumentationRegistry.getContext(), null);
+
+    public AssistantTest() {
+        super(Assistant.class);
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        Intent startIntent =
+                new Intent("android.service.notification.NotificationAssistantService");
+        startIntent.setPackage("android.ext.services");
+        bindService(startIntent);
+        mAssistant = getService();
+        mAssistant.setNoMan(mNoMan);
+    }
+
+    private StatusBarNotification generateSbn(String pkg, int uid, NotificationChannel channel,
+            String tag, String groupKey) {
+        Notification n = new Notification.Builder(mContext, channel.getId())
+                .setContentTitle("foo")
+                .setGroup(groupKey)
+                .build();
+
+        StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, 0, tag, uid, uid, n,
+                UserHandle.SYSTEM, null, 0);
+
+        return sbn;
+    }
+
+    private Ranking generateRanking(StatusBarNotification sbn, NotificationChannel channel) {
+        Ranking mockRanking = mock(Ranking.class);
+        when(mockRanking.getChannel()).thenReturn(channel);
+        when(mockRanking.getImportance()).thenReturn(channel.getImportance());
+        when(mockRanking.getKey()).thenReturn(sbn.getKey());
+        when(mockRanking.getOverrideGroupKey()).thenReturn(null);
+        return mockRanking;
+    }
+
+    private void almostBlockChannel(String pkg, int uid, NotificationChannel channel) {
+        for (int i = 0; i < ChannelImpressions.STREAK_LIMIT; i++) {
+            dismissBadNotification(pkg, uid, channel, String.valueOf(i));
+        }
+    }
+
+    private void dismissBadNotification(String pkg, int uid, NotificationChannel channel,
+            String tag) {
+        StatusBarNotification sbn = generateSbn(pkg, uid, channel, tag, null);
+        mAssistant.setFakeRanking(generateRanking(sbn, channel));
+        mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+        mAssistant.setFakeRanking(mock(Ranking.class));
+        NotificationStats stats = new NotificationStats();
+        stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
+        stats.setSeen();
+        mAssistant.onNotificationRemoved(
+                sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
+    }
+
+    @Test
+    public void testNoAdjustmentForInitialPost() throws Exception {
+        StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, null, null);
+
+        mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
+        mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+        verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+    }
+
+    @Test
+    public void testTriggerAdjustment() throws Exception {
+        almostBlockChannel(PKG1, UID1, P1C1);
+        dismissBadNotification(PKG1, UID1, P1C1, "trigger!");
+
+        StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
+        mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
+        mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+        ArgumentCaptor<Adjustment> captor = ArgumentCaptor.forClass(Adjustment.class);
+        verify(mNoMan, times(1)).applyAdjustmentFromAssistant(any(), captor.capture());
+        assertEquals(sbn.getKey(), captor.getValue().getKey());
+        assertEquals(Ranking.USER_SENTIMENT_NEGATIVE,
+                captor.getValue().getSignals().getInt(Adjustment.KEY_USER_SENTIMENT));
+    }
+
+    @Test
+    public void testMinCannotTriggerAdjustment() throws Exception {
+        almostBlockChannel(PKG1, UID1, P1C3);
+        dismissBadNotification(PKG1, UID1, P1C3, "trigger!");
+
+        StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "new one!", null);
+        mAssistant.setFakeRanking(generateRanking(sbn, P1C3));
+        mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+        verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+    }
+
+    @Test
+    public void testGroupCannotTriggerAdjustment() throws Exception {
+        almostBlockChannel(PKG1, UID1, P1C1);
+
+        StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", "I HAVE A GROUP");
+        mAssistant.setFakeRanking(mock(Ranking.class));
+        NotificationStats stats = new NotificationStats();
+        stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
+        stats.setSeen();
+        mAssistant.onNotificationRemoved(
+                sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
+
+        sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
+        mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+        verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+    }
+
+    @Test
+    public void testAodCannotTriggerAdjustment() throws Exception {
+        almostBlockChannel(PKG1, UID1, P1C1);
+
+        StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
+        mAssistant.setFakeRanking(mock(Ranking.class));
+        NotificationStats stats = new NotificationStats();
+        stats.setDismissalSurface(NotificationStats.DISMISSAL_AOD);
+        stats.setSeen();
+        mAssistant.onNotificationRemoved(
+                sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
+
+        sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
+        mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+        verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+    }
+
+    @Test
+    public void testInteractedCannotTriggerAdjustment() throws Exception {
+        almostBlockChannel(PKG1, UID1, P1C1);
+
+        StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
+        mAssistant.setFakeRanking(mock(Ranking.class));
+        NotificationStats stats = new NotificationStats();
+        stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
+        stats.setSeen();
+        stats.setExpanded();
+        mAssistant.onNotificationRemoved(
+                sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
+
+        sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
+        mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+        verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+    }
+
+    @Test
+    public void testAppDismissedCannotTriggerAdjustment() throws Exception {
+        almostBlockChannel(PKG1, UID1, P1C1);
+
+        StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
+        mAssistant.setFakeRanking(mock(Ranking.class));
+        NotificationStats stats = new NotificationStats();
+        stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
+        stats.setSeen();
+        mAssistant.onNotificationRemoved(
+                sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_APP_CANCEL);
+
+        sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
+        mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+        verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+    }
+
+    @Test
+    public void testAppSeparation() throws Exception {
+        almostBlockChannel(PKG1, UID1, P1C1);
+        dismissBadNotification(PKG1, UID1, P1C1, "trigger!");
+
+        StatusBarNotification sbn = generateSbn(PKG2, UID2, P2C1, "new app!", null);
+        mAssistant.setFakeRanking(generateRanking(sbn, P2C1));
+        mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+        verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+    }
+
+    @Test
+    public void testChannelSeparation() throws Exception {
+        almostBlockChannel(PKG1, UID1, P1C1);
+        dismissBadNotification(PKG1, UID1, P1C1, "trigger!");
+
+        StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C2, "new app!", null);
+        mAssistant.setFakeRanking(generateRanking(sbn, P1C2));
+        mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+        verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+    }
+}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java
new file mode 100644
index 0000000..a8c9fa3
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java
@@ -0,0 +1,85 @@
+/**
+ * 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.ext.services.notification;
+
+import static android.ext.services.notification.ChannelImpressions.STREAK_LIMIT;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class ChannelImpressionsTest {
+
+    @Test
+    public void testNoResultNoBlock() {
+        ChannelImpressions ci = new ChannelImpressions();
+        assertFalse(ci.shouldTriggerBlock());
+    }
+
+    @Test
+    public void testNoStreakNoBlock() {
+        ChannelImpressions ci = new ChannelImpressions();
+
+        for (int i = 0; i < STREAK_LIMIT - 1; i++) {
+            ci.incrementViews();
+            ci.incrementDismissals();
+        }
+
+        assertFalse(ci.shouldTriggerBlock());
+    }
+
+    @Test
+    public void testNoStreakNoBlock_breakStreak() {
+        ChannelImpressions ci = new ChannelImpressions();
+
+        for (int i = 0; i < STREAK_LIMIT; i++) {
+            ci.incrementViews();
+            ci.incrementDismissals();
+            if (i == STREAK_LIMIT - 1) {
+                ci.resetStreak();
+            }
+        }
+
+        assertFalse(ci.shouldTriggerBlock());
+    }
+
+    @Test
+    public void testStreakBlock() {
+        ChannelImpressions ci = new ChannelImpressions();
+
+        for (int i = 0; i <= STREAK_LIMIT; i++) {
+            ci.incrementViews();
+            ci.incrementDismissals();
+        }
+
+        assertTrue(ci.shouldTriggerBlock());
+    }
+
+    @Test
+    public void testRatio_NoBlockEvenWithStreak() {
+        ChannelImpressions ci = new ChannelImpressions();
+
+        for (int i = 0; i < STREAK_LIMIT; i++) {
+            ci.incrementViews();
+            ci.incrementDismissals();
+            ci.incrementViews();
+        }
+
+        assertFalse(ci.shouldTriggerBlock());
+    }
+}
diff --git a/packages/PrintSpooler/res/values-vi/strings.xml b/packages/PrintSpooler/res/values-vi/strings.xml
index a181424..6e8c7a8 100644
--- a/packages/PrintSpooler/res/values-vi/strings.xml
+++ b/packages/PrintSpooler/res/values-vi/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
-    <string name="more_options_button" msgid="2243228396432556771">"Thêm tùy chọn"</string>
+    <string name="more_options_button" msgid="2243228396432556771">"Tùy chọn khác"</string>
     <string name="label_destination" msgid="9132510997381599275">"Đích"</string>
     <string name="label_copies" msgid="3634531042822968308">"Bản sao"</string>
     <string name="label_copies_summary" msgid="3861966063536529540">"Bản sao:"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index fbfa725..b728695 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -289,7 +289,8 @@
     <string name="launch_defaults_some">Some defaults set</string>
     <!-- Launch defaults preference summary with none set [CHAR LIMIT=40] -->
     <string name="launch_defaults_none">No defaults set</string>
-
+    <!-- DO NOT TRANSLATE Empty summary for dynamic preferences -->
+    <string name="summary_empty" translatable="false"></string>
     <!-- Text-To-Speech (TTS) settings --><skip />
     <!-- Name of the TTS package as listed by the package manager. -->
     <string name="tts_settings">Text-to-speech settings</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
index 47cbb77..6aae226 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
@@ -28,17 +28,20 @@
 import android.support.v7.preference.TwoStatePreference;
 import android.text.TextUtils;
 
-import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.core.ConfirmationDialogController;
 
-public abstract class AbstractEnableAdbPreferenceController extends AbstractPreferenceController
-        implements ConfirmationDialogController {
+public abstract class AbstractEnableAdbPreferenceController extends
+        DeveloperOptionsPreferenceController implements ConfirmationDialogController {
     private static final String KEY_ENABLE_ADB = "enable_adb";
     public static final String ACTION_ENABLE_ADB_STATE_CHANGED =
             "com.android.settingslib.development.AbstractEnableAdbController."
                     + "ENABLE_ADB_STATE_CHANGED";
 
-    private SwitchPreference mPreference;
+    public static final int ADB_SETTING_ON = 1;
+    public static final int ADB_SETTING_OFF = 0;
+
+
+    protected SwitchPreference mPreference;
 
     public AbstractEnableAdbPreferenceController(Context context) {
         super(context);
@@ -64,12 +67,13 @@
 
     private boolean isAdbEnabled() {
         final ContentResolver cr = mContext.getContentResolver();
-        return Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) != 0;
+        return Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, ADB_SETTING_OFF)
+                != ADB_SETTING_OFF;
     }
 
     @Override
     public void updateState(Preference preference) {
-        ((TwoStatePreference)preference).setChecked(isAdbEnabled());
+        ((TwoStatePreference) preference).setChecked(isAdbEnabled());
     }
 
     public void enablePreference(boolean enabled) {
@@ -105,7 +109,7 @@
 
     protected void writeAdbSetting(boolean enabled) {
         Settings.Global.putInt(mContext.getContentResolver(),
-                Settings.Global.ADB_ENABLED, enabled ? 1 : 0);
+                Settings.Global.ADB_ENABLED, enabled ? ADB_SETTING_ON : ADB_SETTING_OFF);
         notifyStateChanged();
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
index c167723..7998b2e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
@@ -26,10 +26,9 @@
 import android.support.v7.preference.PreferenceScreen;
 
 import com.android.settingslib.R;
-import com.android.settingslib.core.AbstractPreferenceController;
 
-public abstract class AbstractLogdSizePreferenceController extends AbstractPreferenceController
-        implements Preference.OnPreferenceChangeListener {
+public abstract class AbstractLogdSizePreferenceController extends
+        DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener {
     public static final String ACTION_LOGD_SIZE_UPDATED = "com.android.settingslib.development."
             + "AbstractLogdSizePreferenceController.LOGD_SIZE_UPDATED";
     public static final String EXTRA_CURRENT_LOGD_VALUE = "CURRENT_LOGD_VALUE";
@@ -57,11 +56,6 @@
     }
 
     @Override
-    public boolean isAvailable() {
-        return true;
-    }
-
-    @Override
     public String getPreferenceKey() {
         return SELECT_LOGD_SIZE_KEY;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
index 502fb17..67553adc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
@@ -30,16 +30,15 @@
 import android.text.TextUtils;
 
 import com.android.settingslib.R;
-import com.android.settingslib.core.AbstractPreferenceController;
 import com.android.settingslib.core.ConfirmationDialogController;
 import com.android.settingslib.core.lifecycle.Lifecycle;
 import com.android.settingslib.core.lifecycle.LifecycleObserver;
 import com.android.settingslib.core.lifecycle.events.OnCreate;
 import com.android.settingslib.core.lifecycle.events.OnDestroy;
 
-public abstract class AbstractLogpersistPreferenceController extends AbstractPreferenceController
-        implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnCreate, OnDestroy,
-        ConfirmationDialogController {
+public abstract class AbstractLogpersistPreferenceController extends
+        DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
+        LifecycleObserver, OnCreate, OnDestroy, ConfirmationDialogController {
 
     private static final String SELECT_LOGPERSIST_KEY = "select_logpersist";
     private static final String SELECT_LOGPERSIST_PROPERTY = "persist.logd.logpersistd";
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
new file mode 100644
index 0000000..f68c04f
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
@@ -0,0 +1,76 @@
+/*
+ * 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.settingslib.development;
+
+import android.content.Context;
+
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * This controller is used handle changes for the master switch in the developer options page.
+ *
+ * All Preference Controllers that are a part of the developer options page should inherit this
+ * class.
+ */
+public abstract class DeveloperOptionsPreferenceController extends
+        AbstractPreferenceController {
+
+    public DeveloperOptionsPreferenceController(Context context) {
+        super(context);
+    }
+
+    /**
+     * Child classes should override this method to create custom logic for hiding preferences.
+     *
+     * @return true if the preference is to be displayed.
+     */
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    /**
+     * Called when developer options is enabled
+     */
+    public void onDeveloperOptionsEnabled() {
+        if (isAvailable()) {
+            onDeveloperOptionsSwitchEnabled();
+        }
+    }
+
+    /**
+     * Called when developer options is disabled
+     */
+    public void onDeveloperOptionsDisabled() {
+        if (isAvailable()) {
+            onDeveloperOptionsSwitchDisabled();
+        }
+    }
+
+    /**
+     * Called when developer options is enabled and the preference is available
+     */
+    protected void onDeveloperOptionsSwitchEnabled() {
+    }
+
+    /**
+     * Called when developer options is disabled and the preference is available
+     */
+    protected void onDeveloperOptionsSwitchDisabled() {
+    }
+
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index ec6f831..67fb4d9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -720,9 +720,6 @@
                 Settings.Global.DEVICE_IDLE_CONSTANTS,
                 GlobalSettingsProto.DEVICE_IDLE_CONSTANTS);
         dumpSetting(s, p,
-                Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH,
-                GlobalSettingsProto.DEVICE_IDLE_CONSTANTS_WATCH);
-        dumpSetting(s, p,
                 Settings.Global.APP_IDLE_CONSTANTS,
                 GlobalSettingsProto.APP_IDLE_CONSTANTS);
         dumpSetting(s, p,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 87971cb..7b8d0db 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -273,6 +273,8 @@
             </intent-filter>
             <meta-data android:name="com.android.settings.category"
                     android:value="com.android.settings.category.ia.system" />
+            <meta-data android:name="com.android.settings.summary"
+                    android:resource="@string/summary_empty"/>
         </activity>
 
         <activity-alias android:name=".DemoMode"
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index c49dd4f..29e862d 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -21,14 +21,14 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="3171996292755059205">"Zaključavanje tastature"</string>
-    <string name="keyguard_password_enter_pin_code" msgid="3420548423949593123">"Upišite PIN kôd"</string>
-    <string name="keyguard_password_enter_puk_code" msgid="670683628782925409">"Upišite PUK kôd za SIM karticu i novi PIN kôd"</string>
+    <string name="keyguard_password_enter_pin_code" msgid="3420548423949593123">"Upišite PIN"</string>
+    <string name="keyguard_password_enter_puk_code" msgid="670683628782925409">"Upišite PUK kôd za SIM karticu i novi PIN"</string>
     <string name="keyguard_password_enter_puk_prompt" msgid="3747778500166059332">"PUK kôd za SIM karticu"</string>
-    <string name="keyguard_password_enter_pin_prompt" msgid="8188243197504453830">"Novi PIN kôd za SIM karticu"</string>
+    <string name="keyguard_password_enter_pin_prompt" msgid="8188243197504453830">"Novi PIN za SIM karticu"</string>
     <string name="keyguard_password_entry_touch_hint" msgid="5790410752696806482"><font size="17">"Dodirnite da upišete lozinku"</font></string>
     <string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Upišite lozinku za otključavanje"</string>
-    <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Upišite PIN kôd za otključavanje"</string>
-    <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Pogrešan PIN kôd."</string>
+    <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Upišite PIN za otključavanje"</string>
+    <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Pogrešan PIN."</string>
     <string name="keyguard_charged" msgid="2222329688813033109">"Napunjeno"</string>
     <string name="keyguard_plugged_in" msgid="89308975354638682">"Punjenje"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Brzo punjenje"</string>
@@ -46,8 +46,8 @@
     <string name="keyguard_sim_locked_message" msgid="953766009432168127">"SIM kartica je zaključana."</string>
     <string name="keyguard_sim_puk_locked_message" msgid="1772789643694942073">"SIM kartica je zaključana PUK kodom."</string>
     <string name="keyguard_sim_unlock_progress_dialog_message" msgid="3586601150825821675">"Otključavanje SIM kartice…"</string>
-    <string name="keyguard_accessibility_pin_area" msgid="703175752097279029">"Prostor za PIN kôd"</string>
-    <string name="keyguard_accessibility_sim_pin_area" msgid="912702510825058921">"Prostor za PIN kôd za SIM karticu"</string>
+    <string name="keyguard_accessibility_pin_area" msgid="703175752097279029">"Prostor za PIN"</string>
+    <string name="keyguard_accessibility_sim_pin_area" msgid="912702510825058921">"Prostor za PIN za SIM karticu"</string>
     <string name="keyguard_accessibility_sim_puk_area" msgid="136979425761438705">"Prostor za PUK kôd za SIM karticu"</string>
     <string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Naredni alarm je podešen za <xliff:g id="ALARM">%1$s</xliff:g>"</string>
     <string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Izbriši"</string>
@@ -56,29 +56,29 @@
     <string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Zaboravili ste uzorak?"</string>
     <string name="kg_wrong_pattern" msgid="7620081431514773802">"Pogrešan uzorak"</string>
     <string name="kg_wrong_password" msgid="4580683060277329277">"Pogrešna lozinka"</string>
-    <string name="kg_wrong_pin" msgid="4785660766909463466">"Pogrešan PIN kôd"</string>
+    <string name="kg_wrong_pin" msgid="4785660766909463466">"Pogrešan PIN"</string>
     <plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
       <item quantity="one">Pokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> sekundu.</item>
       <item quantity="few">Pokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> sekunde.</item>
       <item quantity="other">Pokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> sekundi.</item>
     </plurals>
     <string name="kg_pattern_instructions" msgid="5547646893001491340">"Nacrtajte uzorak"</string>
-    <string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Unesite PIN kôd SIM kartice."</string>
-    <string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Unesite PIN kôd SIM kartice operatera \"<xliff:g id="CARRIER">%1$s</xliff:g>\""</string>
+    <string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Unesite PIN SIM kartice."</string>
+    <string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Unesite PIN SIM kartice operatera \"<xliff:g id="CARRIER">%1$s</xliff:g>\""</string>
     <string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Onemogućite eSIM karticu za korištenje uređaja bez mobilne usluge."</string>
-    <string name="kg_pin_instructions" msgid="4069609316644030034">"Unesite PIN kôd"</string>
+    <string name="kg_pin_instructions" msgid="4069609316644030034">"Unesite PIN"</string>
     <string name="kg_password_instructions" msgid="136952397352976538">"Unesite lozinku"</string>
     <string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM kartica je sada onemogućena. Unesite PUK kôd da nastavite. Za više informacija obratite se operateru."</string>
     <string name="kg_puk_enter_puk_hint_multi" msgid="1373131883510840794">"Operater SIM kartice \"<xliff:g id="CARRIER">%1$s</xliff:g>\" sada je onemogućen. Unesite PUK kôd da nastavite. Za više informacija obratite se operateru."</string>
-    <string name="kg_puk_enter_pin_hint" msgid="3137789674920391087">"Unesite željeni PIN kôd"</string>
-    <string name="kg_enter_confirm_pin_hint" msgid="3089485999116759671">"Potvrdite željeni PIN kôd"</string>
+    <string name="kg_puk_enter_pin_hint" msgid="3137789674920391087">"Unesite željeni PIN"</string>
+    <string name="kg_enter_confirm_pin_hint" msgid="3089485999116759671">"Potvrdite željeni PIN"</string>
     <string name="kg_sim_unlock_progress_dialog_message" msgid="4471738151810900114">"Otključavanje SIM kartice…"</string>
-    <string name="kg_invalid_sim_pin_hint" msgid="3057533256729513335">"Unesite PIN kôd koji sadrži 4 do 8 brojeva."</string>
+    <string name="kg_invalid_sim_pin_hint" msgid="3057533256729513335">"Unesite PIN koji sadrži 4 do 8 brojeva."</string>
     <string name="kg_invalid_sim_puk_hint" msgid="6003602401368264144">"PUK kôd treba sadržavati najmanje 8 brojeva."</string>
     <string name="kg_invalid_puk" msgid="5399287873762592502">"Ponovo unesite ispravan PUK kôd. Ponovljeni pokušaji će trajno onemogućiti SIM karticu."</string>
-    <string name="kg_invalid_confirm_pin_hint" product="default" msgid="5672736555427444330">"PIN kodovi se ne poklapaju"</string>
+    <string name="kg_invalid_confirm_pin_hint" product="default" msgid="5672736555427444330">"PIN-ovi se ne poklapaju"</string>
     <string name="kg_login_too_many_attempts" msgid="6604574268387867255">"Previše puta ste pokušali otključati uređaj crtanjem uzorka"</string>
-    <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8637788033282252027">"Pogrešno ste unijeli PIN kôd <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
+    <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8637788033282252027">"Pogrešno ste unijeli PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
     <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7724148763268377734">"Pogrešno ste unijeli lozinku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
     <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4820967667848302092">"Pogrešno ste nacrtali svoj uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
     <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1629351522209932316">"Pokušali ste <xliff:g id="NUMBER_0">%1$d</xliff:g> puta neispravno otključati tablet. U slučaju još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, tablet će se vratiti na fabričke postavke i svi podaci će se izbrisati."</string>
@@ -95,11 +95,11 @@
     <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="8476407539834855">"Pokušali ste <xliff:g id="NUMBER">%d</xliff:g> puta neispravno otključati telefon. Poslovni profil će se ukloniti i svi podaci s profila će se izbrisati."</string>
     <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="956706236554092172">"Pogrešno ste nacrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. U slučaju još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, od vas će se tražiti da tablet otključate koristeći račun e-pošte.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="8364140853305528449">"Pogrešno ste nacrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. U slučaju još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, od vas će se tražiti da telefon otključate koristeći račun e-pošte.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
-    <string name="kg_password_wrong_pin_code_pukked" msgid="3389829202093674267">"PIN kôd za SIM karticu je netačan. Za otključavanje uređaja sada se morate obratiti svom operateru."</string>
+    <string name="kg_password_wrong_pin_code_pukked" msgid="3389829202093674267">"PIN za SIM karticu je netačan. Za otključavanje uređaja sada se morate obratiti svom operateru."</string>
     <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="4314341367727055967">
-      <item quantity="one">PIN kôd za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
-      <item quantity="few">PIN kôd za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
-      <item quantity="other">PIN kôd za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
+      <item quantity="one">PIN za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
+      <item quantity="few">PIN za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
+      <item quantity="other">PIN za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
     </plurals>
     <string name="kg_password_wrong_puk_code_dead" msgid="3329017604125179374">"SIM kartica je neupotrebljiva. Obratite se svom operateru."</string>
     <plurals name="kg_password_wrong_puk_code" formatted="false" msgid="2287504898931957513">
@@ -107,20 +107,20 @@
       <item quantity="few">PUK kôd za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva.</item>
       <item quantity="other">PUK kôd za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva.</item>
     </plurals>
-    <string name="kg_password_pin_failed" msgid="8769990811451236223">"Korištenje PIN koda za SIM karticu nije uspjelo!"</string>
+    <string name="kg_password_pin_failed" msgid="8769990811451236223">"Korištenje PIN-a za SIM karticu nije uspjelo!"</string>
     <string name="kg_password_puk_failed" msgid="1331621440873439974">"Korištenje PUK koda za SIM karticu nije uspjelo!"</string>
     <string name="kg_pin_accepted" msgid="7637293533973802143">"Kôd je prihvaćen"</string>
     <string name="keyguard_carrier_default" msgid="4274828292998453695">"Nema mreže."</string>
     <string name="accessibility_ime_switch_button" msgid="2695096475319405612">"Promjena načina unosa"</string>
     <string name="airplane_mode" msgid="3807209033737676010">"Način rada u avionu"</string>
     <string name="kg_prompt_reason_restart_pattern" msgid="7246972020562621506">"Potreban je uzorak nakon što se uređaj ponovo pokrene"</string>
-    <string name="kg_prompt_reason_restart_pin" msgid="6303592361322290145">"Potreban je PIN kôd nakon što se uređaj ponovo pokrene"</string>
+    <string name="kg_prompt_reason_restart_pin" msgid="6303592361322290145">"Potreban je PIN nakon što se uređaj ponovo pokrene"</string>
     <string name="kg_prompt_reason_restart_password" msgid="6984641181515902406">"Potrebna je lozinka nakon što se uređaj ponovo pokrene"</string>
     <string name="kg_prompt_reason_timeout_pattern" msgid="5304487696073914063">"Uzorak je potreban radi dodatne sigurnosti"</string>
-    <string name="kg_prompt_reason_timeout_pin" msgid="8851462864335757813">"PIN kôd je potreban radi dodatne sigurnosti"</string>
+    <string name="kg_prompt_reason_timeout_pin" msgid="8851462864335757813">"PIN je potreban radi dodatne sigurnosti"</string>
     <string name="kg_prompt_reason_timeout_password" msgid="6563904839641583441">"Lozinka je potrebna radi dodatne sigurnosti"</string>
     <string name="kg_prompt_reason_switch_profiles_pattern" msgid="3398054847288438444">"Potreban je uzorak nakon prelaska na drugi profil"</string>
-    <string name="kg_prompt_reason_switch_profiles_pin" msgid="7426368139226961699">"Potreban je PIN kôd nakon prelaska na drugi profil"</string>
+    <string name="kg_prompt_reason_switch_profiles_pin" msgid="7426368139226961699">"Potreban je PIN nakon prelaska na drugi profil"</string>
     <string name="kg_prompt_reason_switch_profiles_password" msgid="8383831046318421845">"Potrebna je lozinka nakon prelaska na drugi profil"</string>
     <string name="kg_prompt_reason_device_admin" msgid="3452168247888906179">"Uređaj je zaključao administrator"</string>
     <string name="kg_prompt_reason_user_request" msgid="8236951765212462286">"Uređaj je ručno zaključan"</string>
@@ -130,9 +130,9 @@
       <item quantity="other">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite uzorak.</item>
     </plurals>
     <plurals name="kg_prompt_reason_time_pin" formatted="false" msgid="34586942088144385">
-      <item quantity="one">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sat. Potvrdite PIN kôd.</item>
-      <item quantity="few">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sata. Potvrdite PIN kôd.</item>
-      <item quantity="other">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite PIN kôd.</item>
+      <item quantity="one">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sat. Potvrdite PIN.</item>
+      <item quantity="few">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sata. Potvrdite PIN.</item>
+      <item quantity="other">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite PIN.</item>
     </plurals>
     <plurals name="kg_prompt_reason_time_password" formatted="false" msgid="257297696215346527">
       <item quantity="one">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sat. Potvrdite lozinku.</item>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 38ac499..d3a15df 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -758,7 +758,7 @@
     <string name="lockscreen_unlock_left" msgid="2043092136246951985">"Zkratka vlevo také odemyká"</string>
     <string name="lockscreen_unlock_right" msgid="1529992940510318775">"Zkratka vpravo také odemyká"</string>
     <string name="lockscreen_none" msgid="4783896034844841821">"Žádné"</string>
-    <string name="tuner_launch_app" msgid="1527264114781925348">"Spustit aplikaci <xliff:g id="APP">%1$s</xliff:g>"</string>
+    <string name="tuner_launch_app" msgid="1527264114781925348">"Do aplikace <xliff:g id="APP">%1$s</xliff:g>"</string>
     <string name="tuner_other_apps" msgid="4726596850501162493">"Další aplikace"</string>
     <string name="tuner_circle" msgid="2340998864056901350">"Kruh"</string>
     <string name="tuner_plus" msgid="6792960658533229675">"Plus"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 70ee57a..62d668c 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -288,7 +288,7 @@
     <string name="quick_settings_location_off_label" msgid="7464544086507331459">"Localización desactivada"</string>
     <string name="quick_settings_media_device_label" msgid="1302906836372603762">"Dispositivo multimedia"</string>
     <string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
-    <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"Só chamadas de emerxencia"</string>
+    <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"Só chamadas de urxencia"</string>
     <string name="quick_settings_settings_label" msgid="5326556592578065401">"Configuración"</string>
     <string name="quick_settings_time_label" msgid="4635969182239736408">"Hora"</string>
     <string name="quick_settings_user_label" msgid="5238995632130897840">"Eu"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 89ea9f1..6b79e33 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -79,7 +79,7 @@
     <string name="usb_preference_title" msgid="6551050377388882787">"USB ဖိုင်ပြောင်း ရွေးမှုများ"</string>
     <string name="use_mtp_button_title" msgid="4333504413563023626">"မီဒီယာပလေရာအနေဖြင့် တပ်ဆင်ရန် (MTP)"</string>
     <string name="use_ptp_button_title" msgid="7517127540301625751">"ကင်မရာအနေဖြင့် တပ်ဆင်ရန် (PTP)"</string>
-    <string name="installer_cd_button_title" msgid="2312667578562201583">"Macအတွက်Andriodဖိုင်ပြောင်းအပ်ပလီကေးရှင်းထည့်ခြင်း"</string>
+    <string name="installer_cd_button_title" msgid="2312667578562201583">"Mac အတွက် Android File Transfer အက်ပ်ထည့်ခြင်း"</string>
     <string name="accessibility_back" msgid="567011538994429120">"နောက်သို့"</string>
     <string name="accessibility_home" msgid="8217216074895377641">"ပင်မစာမျက်နှာ"</string>
     <string name="accessibility_menu" msgid="316839303324695949">"မီနူး"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 58243b4..f8996aa 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.pip.phone;
 
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -73,7 +72,7 @@
      */
     TaskStackListener mTaskStackListener = new TaskStackListener() {
         @Override
-        public void onActivityPinned(String packageName, int userId, int taskId) {
+        public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
             mTouchHandler.onActivityPinned();
             mMediaController.onActivityPinned();
             mMenuController.onActivityPinned();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index d71d4de..9fb201b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.pip.phone;
 
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index eef0e7d..21a836c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.pip.phone;
 
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -161,13 +161,7 @@
         mMenuController.hideMenuWithoutResize();
         mHandler.post(() -> {
             try {
-                if (skipAnimation) {
-                    mActivityManager.moveTasksToFullscreenStack(PINNED_STACK_ID, true /* onTop */);
-                } else {
-                    mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */,
-                            true /* allowResizeInDockedMode */, true /* preserveWindows */,
-                            true /* animate */, EXPAND_STACK_TO_FULLSCREEN_DURATION);
-                }
+                mActivityManager.dismissPip(!skipAnimation, EXPAND_STACK_TO_FULLSCREEN_DURATION);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error expanding PiP activity", e);
             }
@@ -185,7 +179,7 @@
         mMenuController.hideMenuWithoutResize();
         mHandler.post(() -> {
             try {
-                mActivityManager.removeStack(PINNED_STACK_ID);
+                mActivityManager.removeStacksInWindowingModes(new int[]{ WINDOWING_MODE_PINNED });
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to remove PiP", e);
             }
@@ -540,7 +534,7 @@
                         return true;
                     }
 
-                    mActivityManager.resizeStack(PINNED_STACK_ID, toBounds,
+                    mActivityManager.resizeStack(stackInfo.stackId, toBounds,
                             false /* allowResizeInDockedMode */, true /* preserveWindows */,
                             true /* animate */, duration);
                     mBounds.set(toBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
index 84410f2..2f53de9 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.pip.phone;
 
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 6e85549..e0445c1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -53,7 +53,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -123,6 +123,7 @@
     private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
     private boolean mInitialized;
     private int mPipTaskId = TASK_ID_NO_PIP;
+    private int mPinnedStackId = INVALID_STACK_ID;
     private ComponentName mPipComponentName;
     private MediaController mPipMediaController;
     private String[] mLastPackagesResourceGranted;
@@ -338,9 +339,11 @@
         mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener);
         if (removePipStack) {
             try {
-                mActivityManager.removeStack(PINNED_STACK_ID);
+                mActivityManager.removeStack(mPinnedStackId);
             } catch (RemoteException e) {
                 Log.e(TAG, "removeStack failed", e);
+            } finally {
+                mPinnedStackId = INVALID_STACK_ID;
             }
         }
         for (int i = mListeners.size() - 1; i >= 0; --i) {
@@ -426,7 +429,7 @@
         }
         try {
             int animationDurationMs = -1;
-            mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds,
+            mActivityManager.resizeStack(mPinnedStackId, mCurrentPipBounds,
                     true, true, true, animationDurationMs);
         } catch (RemoteException e) {
             Log.e(TAG, "resizeStack failed", e);
@@ -657,7 +660,7 @@
         }
 
         @Override
-        public void onActivityPinned(String packageName, int userId, int taskId) {
+        public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
             if (DEBUG) Log.d(TAG, "onActivityPinned()");
             if (!checkCurrentUserId(mContext, DEBUG)) {
                 return;
@@ -668,6 +671,7 @@
                 return;
             }
             if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
+            mPinnedStackId = stackInfo.stackId;
             mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
             mPipComponentName = ComponentName.unflattenFromString(
                     stackInfo.taskNames[stackInfo.taskNames.length - 1]);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index dc83333..3e2a5f3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -16,9 +16,9 @@
 
 package com.android.systemui.recents;
 
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.view.View.MeasureSpec;
 
 import android.app.ActivityManager;
@@ -173,7 +173,7 @@
         }
 
         @Override
-        public void onActivityPinned(String packageName, int userId, int taskId) {
+        public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
             // Check this is for the right user
             if (!checkCurrentUserId(mContext, false /* debug */)) {
                 return;
@@ -873,7 +873,9 @@
             getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo runningTask,
                     Rect windowOverrideRect) {
         final boolean isLowRamDevice = Recents.getConfiguration().isLowRamDevice;
-        if (runningTask != null && runningTask.stackId == FREEFORM_WORKSPACE_STACK_ID) {
+        if (runningTask != null
+                && runningTask.configuration.windowConfiguration.getWindowingMode()
+                == WINDOWING_MODE_FREEFORM) {
             ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>();
             ArrayList<Task> tasks = mDummyStackView.getStack().getStackTasks();
             TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 4580b1f..bddf9a5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.recents.misc;
 
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -177,7 +175,7 @@
         public void onTaskStackChangedBackground() { }
         public void onTaskStackChanged() { }
         public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
-        public void onActivityPinned(String packageName, int userId, int taskId) { }
+        public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { }
         public void onActivityUnpinned() { }
         public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
         public void onPinnedStackAnimationStarted() { }
@@ -232,10 +230,11 @@
         }
 
         @Override
-        public void onActivityPinned(String packageName, int userId, int taskId)
+        public void onActivityPinned(String packageName, int userId, int taskId, int stackId)
                 throws RemoteException {
             mHandler.removeMessages(H.ON_ACTIVITY_PINNED);
-            mHandler.obtainMessage(H.ON_ACTIVITY_PINNED, userId, taskId, packageName).sendToTarget();
+            mHandler.obtainMessage(H.ON_ACTIVITY_PINNED,
+                    new PinnedActivityInfo(packageName, userId, taskId, stackId)).sendToTarget();
         }
 
         @Override
@@ -618,13 +617,6 @@
     }
 
     /**
-     * Returns whether the given stack id is the freeform workspace stack id.
-     */
-    public static boolean isFreeformStack(int stackId) {
-        return stackId == FREEFORM_WORKSPACE_STACK_ID;
-    }
-
-    /**
      * @return whether there are any docked tasks for the current user.
      */
     public boolean hasDockedTask() {
@@ -734,14 +726,12 @@
         }
     }
 
-    /**
-     * Moves a task into another stack.
-     */
-    public void moveTaskToStack(int taskId, int stackId) {
+    /** Set the task's windowing mode. */
+    public void setTaskWindowingMode(int taskId, int windowingMode) {
         if (mIam == null) return;
 
         try {
-            mIam.positionTaskInStack(taskId, stackId, 0);
+            mIam.setTaskWindowingMode(taskId, windowingMode, false /* onTop */);
         } catch (RemoteException | IllegalArgumentException e) {
             e.printStackTrace();
         }
@@ -1132,7 +1122,7 @@
         if (mIam == null) {
             return;
         }
-        if (taskKey.stackId == DOCKED_STACK_ID) {
+        if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
             // We show non-visible docked tasks in Recents, but we always want to launch
             // them in the fullscreen stack.
             if (options == null) {
@@ -1314,6 +1304,20 @@
         void onStartActivityResult(boolean succeeded);
     }
 
+    private class PinnedActivityInfo {
+        final String mPackageName;
+        final int mUserId;
+        final int mTaskId;
+        final int mStackId;
+
+        PinnedActivityInfo(String packageName, int userId, int taskId, int stackId) {
+            mPackageName = packageName;
+            mUserId = userId;
+            mTaskId = taskId;
+            mStackId = stackId;
+        }
+    }
+
     private final class H extends Handler {
         private static final int ON_TASK_STACK_CHANGED = 1;
         private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
@@ -1349,9 +1353,10 @@
                         break;
                     }
                     case ON_ACTIVITY_PINNED: {
+                        final PinnedActivityInfo info = (PinnedActivityInfo) msg.obj;
                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
-                            mTaskStackListeners.get(i).onActivityPinned((String) msg.obj, msg.arg1,
-                                    msg.arg2);
+                            mTaskStackListeners.get(i).onActivityPinned(
+                                    info.mPackageName, info.mUserId, info.mTaskId, info.mStackId);
                         }
                         break;
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/HighResThumbnailLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/HighResThumbnailLoader.java
index 48fa6c3..6414ea1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/HighResThumbnailLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/HighResThumbnailLoader.java
@@ -187,7 +187,7 @@
     }
 
     @Override
-    public void onTaskStackIdChanged() {
+    public void onTaskWindowingModeChanged() {
     }
 
     private final Runnable mLoader = new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 8d31730..d5e0313 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.recents.model;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -155,12 +157,13 @@
             ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
 
             // Compose the task key
-            Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
+            final int windowingMode = t.configuration.windowConfiguration.getWindowingMode();
+            Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, windowingMode, t.baseIntent,
                     t.userId, t.firstActiveTime, t.lastActiveTime);
 
             // This task is only shown in the stack if it satisfies the historical time or min
             // number of tasks constraints. Freeform tasks are also always shown.
-            boolean isFreeformTask = SystemServicesProxy.isFreeformStack(t.stackId);
+            boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM;
             boolean isStackTask;
             if (Recents.getConfiguration().isGridEnabled) {
                 // When grid layout is enabled, we only show the first
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 9e6bf85..abdb5cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.recents.model;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -46,8 +48,8 @@
         public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData);
         /* Notifies when a task has been unbound */
         public void onTaskDataUnloaded();
-        /* Notifies when a task's stack id has changed. */
-        public void onTaskStackIdChanged();
+        /* Notifies when a task's windowing mode has changed. */
+        public void onTaskWindowingModeChanged();
     }
 
     /* The Task Key represents the unique primary key for the task */
@@ -55,7 +57,7 @@
         @ViewDebug.ExportedProperty(category="recents")
         public final int id;
         @ViewDebug.ExportedProperty(category="recents")
-        public int stackId;
+        public int windowingMode;
         @ViewDebug.ExportedProperty(category="recents")
         public final Intent baseIntent;
         @ViewDebug.ExportedProperty(category="recents")
@@ -67,10 +69,10 @@
 
         private int mHashCode;
 
-        public TaskKey(int id, int stackId, Intent intent, int userId, long firstActiveTime,
+        public TaskKey(int id, int windowingMode, Intent intent, int userId, long firstActiveTime,
                 long lastActiveTime) {
             this.id = id;
-            this.stackId = stackId;
+            this.windowingMode = windowingMode;
             this.baseIntent = intent;
             this.userId = userId;
             this.firstActiveTime = firstActiveTime;
@@ -78,8 +80,8 @@
             updateHashCode();
         }
 
-        public void setStackId(int stackId) {
-            this.stackId = stackId;
+        public void setWindowingMode(int windowingMode) {
+            this.windowingMode = windowingMode;
             updateHashCode();
         }
 
@@ -93,7 +95,9 @@
                 return false;
             }
             TaskKey otherKey = (TaskKey) o;
-            return id == otherKey.id && stackId == otherKey.stackId && userId == otherKey.userId;
+            return id == otherKey.id
+                    && windowingMode == otherKey.windowingMode
+                    && userId == otherKey.userId;
         }
 
         @Override
@@ -103,12 +107,12 @@
 
         @Override
         public String toString() {
-            return "id=" + id + " stackId=" + stackId + " user=" + userId + " lastActiveTime=" +
-                    lastActiveTime;
+            return "id=" + id + " windowingMode=" + windowingMode + " user=" + userId
+                    + " lastActiveTime=" + lastActiveTime;
         }
 
         private void updateHashCode() {
-            mHashCode = Objects.hash(id, stackId, userId);
+            mHashCode = Objects.hash(id, windowingMode, userId);
         }
     }
 
@@ -277,14 +281,12 @@
         this.group = group;
     }
 
-    /**
-     * Updates the stack id of this task.
-     */
-    public void setStackId(int stackId) {
-        key.setStackId(stackId);
+    /** Updates the task's windowing mode. */
+    public void setWindowingMode(int windowingMode) {
+        key.setWindowingMode(windowingMode);
         int callbackCount = mCallbacks.size();
         for (int i = 0; i < callbackCount; i++) {
-            mCallbacks.get(i).onTaskStackIdChanged();
+            mCallbacks.get(i).onTaskWindowingModeChanged();
         }
     }
 
@@ -293,7 +295,7 @@
      */
     public boolean isFreeformTask() {
         SystemServicesProxy ssp = Recents.getSystemServices();
-        return ssp.hasFreeformWorkspaceSupport() && ssp.isFreeformStack(key.stackId);
+        return ssp.hasFreeformWorkspaceSupport() && key.windowingMode == WINDOWING_MODE_FREEFORM;
     }
 
     /** Notifies the callback listeners that this task has been loaded */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyCache.java
index be99f93..247a654 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyCache.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyCache.java
@@ -45,7 +45,7 @@
     final V getAndInvalidateIfModified(Task.TaskKey key) {
         Task.TaskKey lastKey = mKeys.get(key.id);
         if (lastKey != null) {
-            if ((lastKey.stackId != key.stackId) ||
+            if ((lastKey.windowingMode != key.windowingMode) ||
                     (lastKey.lastActiveTime != key.lastActiveTime)) {
                 // The task has updated (been made active since the last time it was put into the
                 // LRU cache) or the stack id for the task has changed, invalidate that cache item
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 6e3be09..fdae917 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -18,8 +18,8 @@
 
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_LEFT;
@@ -115,7 +115,7 @@
     /**
      * Moves the given task.
      */
-    public void moveTaskToStack(Task task, int insertIndex, int newStackId) {
+    public void setTaskWindowingMode(Task task, int insertIndex, int windowingMode) {
         int taskIndex = indexOf(task);
         if (taskIndex != insertIndex) {
             mTasks.remove(taskIndex);
@@ -127,7 +127,7 @@
 
         // Update the stack id now, after we've moved the task, and before we update the
         // filtered tasks
-        task.setStackId(newStackId);
+        task.setWindowingMode(windowingMode);
         updateFilteredTasks();
     }
 
@@ -590,17 +590,15 @@
         mCb = cb;
     }
 
-    /**
-     * Moves the given task to either the front of the freeform workspace or the stack.
-     */
-    public void moveTaskToStack(Task task, int newStackId) {
+    /** Sets the windowing mode for a given task. */
+    public void setTaskWindowingMode(Task task, int windowingMode) {
         // Find the index to insert into
         ArrayList<Task> taskList = mStackTaskList.getTasks();
         int taskCount = taskList.size();
-        if (!task.isFreeformTask() && (newStackId == FREEFORM_WORKSPACE_STACK_ID)) {
+        if (!task.isFreeformTask() && (windowingMode == WINDOWING_MODE_FREEFORM)) {
             // Insert freeform tasks at the front
-            mStackTaskList.moveTaskToStack(task, taskCount, newStackId);
-        } else if (task.isFreeformTask() && (newStackId == FULLSCREEN_WORKSPACE_STACK_ID)) {
+            mStackTaskList.setTaskWindowingMode(task, taskCount, windowingMode);
+        } else if (task.isFreeformTask() && (windowingMode == WINDOWING_MODE_FULLSCREEN)) {
             // Insert after the first stacked task
             int insertIndex = 0;
             for (int i = taskCount - 1; i >= 0; i--) {
@@ -609,7 +607,7 @@
                     break;
                 }
             }
-            mStackTaskList.moveTaskToStack(task, insertIndex, newStackId);
+            mStackTaskList.setTaskWindowingMode(task, insertIndex, windowingMode);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 7ede0c5..3160ee0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -16,9 +16,8 @@
 
 package com.android.systemui.recents.views;
 
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -2094,18 +2093,18 @@
         }
 
         boolean isFreeformTask = event.task.isFreeformTask();
-        boolean hasChangedStacks =
+        boolean hasChangedWindowingMode =
                 (!isFreeformTask && event.dropTarget == mFreeformWorkspaceDropTarget) ||
                         (isFreeformTask && event.dropTarget == mStackDropTarget);
 
-        if (hasChangedStacks) {
+        if (hasChangedWindowingMode) {
             // Move the task to the right position in the stack (ie. the front of the stack if
             // freeform or the front of the stack if fullscreen). Note, we MUST move the tasks
             // before we update their stack ids, otherwise, the keys will have changed.
             if (event.dropTarget == mFreeformWorkspaceDropTarget) {
-                mStack.moveTaskToStack(event.task, FREEFORM_WORKSPACE_STACK_ID);
+                mStack.setTaskWindowingMode(event.task, WINDOWING_MODE_FREEFORM);
             } else if (event.dropTarget == mStackDropTarget) {
-                mStack.moveTaskToStack(event.task, FULLSCREEN_WORKSPACE_STACK_ID);
+                mStack.setTaskWindowingMode(event.task, WINDOWING_MODE_FULLSCREEN);
             }
             updateLayoutAlgorithm(true /* boundScroll */);
 
@@ -2114,7 +2113,7 @@
                 @Override
                 public void run() {
                     SystemServicesProxy ssp = Recents.getSystemServices();
-                    ssp.moveTaskToStack(event.task.key.id, event.task.key.stackId);
+                    ssp.setTaskWindowingMode(event.task.key.id, event.task.key.windowingMode);
                 }
             });
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index c64f6df..9d63964 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -647,7 +647,7 @@
     }
 
     @Override
-    public void onTaskStackIdChanged() {
+    public void onTaskWindowingModeChanged() {
         // Force rebind the header, the thumbnail does not change due to stack changes
         mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
         mHeaderView.onTaskDataLoaded();
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index f7c0411..85a6062 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.stackdivider;
 
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.view.WindowManager.DOCKED_INVALID;
 
 import android.app.ActivityManager;
@@ -88,8 +87,7 @@
         @Override
         public void run() {
             try {
-                ActivityManager.getService().moveTasksToFullscreenStack(
-                        DOCKED_STACK_ID, false /* onTop */);
+                ActivityManager.getService().dismissSplitScreenMode(false /* onTop */);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to remove stack: " + e);
             }
@@ -100,8 +98,7 @@
         @Override
         public void run() {
             try {
-                ActivityManager.getService().resizeStack(
-                        DOCKED_STACK_ID, null, true, true, false, -1);
+                ActivityManager.getService().dismissSplitScreenMode(true /* onTop */);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to resize stack: " + e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 2cff79d..6cfd42f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -43,6 +43,7 @@
 import android.util.Log;
 import android.util.Property;
 import android.util.TypedValue;
+import android.view.View;
 import android.view.ViewDebug;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.Interpolator;
@@ -142,6 +143,7 @@
     private float[] mMatrix;
     private ColorMatrixColorFilter mMatrixColorFilter;
     private boolean mIsInShelf;
+    private Runnable mLayoutRunnable;
 
     public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) {
         this(context, slot, sbn, false);
@@ -796,6 +798,24 @@
         }
     }
 
+    /**
+     * This method returns the drawing rect for the view which is different from the regular
+     * drawing rect, since we layout all children at position 0 and usually the translation is
+     * neglected. The standard implementation doesn't account for translation.
+     *
+     * @param outRect The (scrolled) drawing bounds of the view.
+     */
+    @Override
+    public void getDrawingRect(Rect outRect) {
+        super.getDrawingRect(outRect);
+        float translationX = getTranslationX();
+        float translationY = getTranslationY();
+        outRect.left += translationX;
+        outRect.right += translationX;
+        outRect.top += translationY;
+        outRect.bottom += translationY;
+    }
+
     public void setIsInShelf(boolean isInShelf) {
         mIsInShelf = isInShelf;
     }
@@ -804,6 +824,19 @@
         return mIsInShelf;
     }
 
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (mLayoutRunnable != null) {
+            mLayoutRunnable.run();
+            mLayoutRunnable = null;
+        }
+    }
+
+    public void executeOnLayout(Runnable runnable) {
+        mLayoutRunnable = runnable;
+    }
+
     public interface OnVisibilityChangedListener {
         void onVisibilityChanged(int newVisibility);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 021b451..8afb849 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -384,7 +384,7 @@
             if (mDozeParameters.getAlwaysOn()) {
                 // Setting power states can happen after we push out the frame. Make sure we
                 // stay fully opaque until the power state request reaches the lower levels.
-                setDozeInFrontAlphaDelayed(mAodFrontScrimOpacity, 30);
+                setDozeInFrontAlphaDelayed(mAodFrontScrimOpacity, 100);
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 41a69b4..40fe50f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -4,10 +4,8 @@
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Rect;
-import android.graphics.drawable.Icon;
 import android.support.annotation.NonNull;
 import android.support.v4.util.ArrayMap;
-import android.support.v4.util.ArraySet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.FrameLayout;
@@ -269,18 +267,26 @@
      */
     private void applyNotificationIconsTint() {
         for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
-            StatusBarIconView v = (StatusBarIconView) mNotificationIcons.getChildAt(i);
-            boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
-            int color = StatusBarIconView.NO_COLOR;
-            boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
-            if (colorize) {
-                color = DarkIconDispatcher.getTint(mTintArea, v, mIconTint);
+            final StatusBarIconView iv = (StatusBarIconView) mNotificationIcons.getChildAt(i);
+            if (iv.getWidth() != 0) {
+                updateTintForIcon(iv);
+            } else {
+                iv.executeOnLayout(() -> updateTintForIcon(iv));
             }
-            v.setStaticDrawableColor(color);
-            v.setDecorColor(mIconTint);
         }
     }
 
+    private void updateTintForIcon(StatusBarIconView v) {
+        boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
+        int color = StatusBarIconView.NO_COLOR;
+        boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
+        if (colorize) {
+            color = DarkIconDispatcher.getTint(mTintArea, v, mIconTint);
+        }
+        v.setStaticDrawableColor(color);
+        v.setDecorColor(mIconTint);
+    }
+
     public void setDark(boolean dark) {
         mNotificationIcons.setDark(dark, false, 0);
         mShelfIcons.setDark(dark, false, 0);
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 d3bb17f..54be857 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -102,6 +102,7 @@
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
@@ -1818,7 +1819,9 @@
         final int id = n.getId();
         final int userId = n.getUserId();
         try {
-            mBarService.onNotificationClear(pkg, tag, id, userId);
+            // TODO: record actual dismissal surface
+            mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(),
+                    NotificationStats.DISMISSAL_OTHER);
             if (FORCE_REMOTE_INPUT_HISTORY
                     && mKeysKeptForRemoteInput.contains(n.getKey())) {
                 mKeysKeptForRemoteInput.remove(n.getKey());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java
index 5a8c6dd..767e124 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents.model;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.anyBoolean;
@@ -59,7 +60,7 @@
         MockitoAnnotations.initMocks(this);
         mLoader = new HighResThumbnailLoader(mMockSystemServicesProxy, Looper.getMainLooper(),
                 false);
-        mTask.key = new TaskKey(0, 0, null, 0, 0, 0);
+        mTask.key = new TaskKey(0, WINDOWING_MODE_UNDEFINED, null, 0, 0, 0);
         when(mMockSystemServicesProxy.getTaskThumbnail(anyInt(), anyBoolean()))
                 .thenReturn(mThumbnailData);
         mLoader.setVisible(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index c0de004..3b401a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -21,9 +21,11 @@
 
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
 import com.android.internal.app.NightDisplayController;
+import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.Prefs.Key;
 import com.android.systemui.SysuiTestCase;
@@ -45,6 +47,8 @@
 
     @Before
     public void setUp() throws Exception {
+        mDependency.injectTestDependency(Dependency.BG_LOOPER,
+                TestableLooper.get(this).getLooper());
         Prefs.putBoolean(mContext, Key.QS_NIGHTDISPLAY_ADDED, false);
         mQsTileHost = Mockito.mock(QSTileHost.class);
         mAutoTileManager = new AutoTileManager(mContext, mQsTileHost);
diff --git a/packages/VpnDialogs/res/values-cs/strings.xml b/packages/VpnDialogs/res/values-cs/strings.xml
index 7a3d515..47d950f 100644
--- a/packages/VpnDialogs/res/values-cs/strings.xml
+++ b/packages/VpnDialogs/res/values-cs/strings.xml
@@ -31,6 +31,6 @@
     <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Změnit nastavení VPN"</string>
     <string name="configure" msgid="4905518375574791375">"Konfigurovat"</string>
     <string name="disconnect" msgid="971412338304200056">"Odpojit"</string>
-    <string name="open_app" msgid="3717639178595958667">"Spustit aplikaci"</string>
+    <string name="open_app" msgid="3717639178595958667">"Do aplikace"</string>
     <string name="dismiss" msgid="6192859333764711227">"Zavřít"</string>
 </resources>
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index f9f6bf7..b32be73 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -1328,7 +1328,7 @@
     const void* ptr = bitmap.getPixels();
     jlong id = (jlong)(uintptr_t)rsAllocationCreateFromBitmap((RsContext)con,
                                                   (RsType)type, (RsAllocationMipmapControl)mip,
-                                                  ptr, bitmap.getSize(), usage);
+                                                  ptr, bitmap.computeByteSize(), usage);
     return id;
 }
 
@@ -1356,7 +1356,7 @@
     const void* ptr = bitmap.getPixels();
     jlong id = (jlong)(uintptr_t)rsAllocationCubeCreateFromBitmap((RsContext)con,
                                                       (RsType)type, (RsAllocationMipmapControl)mip,
-                                                      ptr, bitmap.getSize(), usage);
+                                                      ptr, bitmap.computeByteSize(), usage);
     return id;
 }
 
@@ -1371,7 +1371,7 @@
     const void* ptr = bitmap.getPixels();
     rsAllocation2DData((RsContext)con, (RsAllocation)alloc, 0, 0,
                        0, RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X,
-                       w, h, ptr, bitmap.getSize(), 0);
+                       w, h, ptr, bitmap.computeByteSize(), 0);
 }
 
 static void
@@ -1381,7 +1381,7 @@
     GraphicsJNI::getSkBitmap(_env, jbitmap, &bitmap);
 
     void* ptr = bitmap.getPixels();
-    rsAllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, ptr, bitmap.getSize());
+    rsAllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, ptr, bitmap.computeByteSize());
     bitmap.notifyPixelsChanged();
 }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 862070a..880f236 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -216,9 +216,12 @@
                 serviceComponent = ComponentName.unflattenFromString(componentName);
                 serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
                         0, mUserId);
+                if (serviceInfo == null) {
+                    Slog.e(TAG, "Bad AutofillService name: " + componentName);
+                }
             } catch (RuntimeException | RemoteException e) {
-                Slog.e(TAG, "Bad autofill service name " + componentName + ": " + e);
-                return;
+                Slog.e(TAG, "Error getting service info for '" + componentName + "': " + e);
+                serviceInfo = null;
             }
         }
         try {
@@ -228,21 +231,24 @@
                 if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo);
             } else {
                 mInfo = null;
-                if (sDebug) Slog.d(TAG, "Reset component for user " + mUserId);
-            }
-            final boolean isEnabled = isEnabled();
-            if (wasEnabled != isEnabled) {
-                if (!isEnabled) {
-                    final int sessionCount = mSessions.size();
-                    for (int i = sessionCount - 1; i >= 0; i--) {
-                        final Session session = mSessions.valueAt(i);
-                        session.removeSelfLocked();
-                    }
+                if (sDebug) {
+                    Slog.d(TAG, "Reset component for user " + mUserId + " (" + componentName + ")");
                 }
-                sendStateToClients(false);
             }
         } catch (Exception e) {
-            Slog.e(TAG, "Bad AutofillService '" + componentName + "': " + e);
+            Slog.e(TAG, "Bad AutofillServiceInfo for '" + componentName + "': " + e);
+            mInfo = null;
+        }
+        final boolean isEnabled = isEnabled();
+        if (wasEnabled != isEnabled) {
+            if (!isEnabled) {
+                final int sessionCount = mSessions.size();
+                for (int i = sessionCount - 1; i >= 0; i--) {
+                    final Session session = mSessions.valueAt(i);
+                    session.removeSelfLocked();
+                }
+            }
+            sendStateToClients(false);
         }
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 11478fe..ed00ffe 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -56,9 +56,11 @@
 import android.service.autofill.FillContext;
 import android.service.autofill.FillRequest;
 import android.service.autofill.FillResponse;
+import android.service.autofill.InternalSanitizer;
 import android.service.autofill.InternalValidator;
 import android.service.autofill.SaveInfo;
 import android.service.autofill.SaveRequest;
+import android.service.autofill.Transformation;
 import android.service.autofill.ValueFinder;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -856,6 +858,8 @@
             return true;
         }
 
+        final ArrayMap<AutofillId, InternalSanitizer> sanitizers = createSanitizers(saveInfo);
+
         // Cache used to make sure changed fields do not belong to a dataset.
         final ArrayMap<AutofillId, AutofillValue> currentValues = new ArrayMap<>();
         final ArraySet<AutofillId> allIds = new ArraySet<>();
@@ -895,6 +899,7 @@
                         break;
                     }
                 }
+                value = getSanitizedValue(sanitizers, id, value);
                 currentValues.put(id, value);
                 final AutofillValue filledValue = viewState.getAutofilledValue();
 
@@ -1037,6 +1042,48 @@
         return true;
     }
 
+    @Nullable
+    private ArrayMap<AutofillId, InternalSanitizer> createSanitizers(@Nullable SaveInfo saveInfo) {
+        if (saveInfo == null) return null;
+
+        final InternalSanitizer[] sanitizerKeys = saveInfo.getSanitizerKeys();
+        if (sanitizerKeys == null) return null;
+
+        final int size = sanitizerKeys.length ;
+        final ArrayMap<AutofillId, InternalSanitizer> sanitizers = new ArrayMap<>(size);
+        if (sDebug) Slog.d(TAG, "Service provided " + size + " sanitizers");
+        final AutofillId[][] sanitizerValues = saveInfo.getSanitizerValues();
+        for (int i = 0; i < size; i++) {
+            final InternalSanitizer sanitizer = sanitizerKeys[i];
+            final AutofillId[] ids = sanitizerValues[i];
+            if (sDebug) {
+                Slog.d(TAG, "sanitizer #" + i + " (" + sanitizer + ") for ids "
+                        + Arrays.toString(ids));
+            }
+            for (AutofillId id : ids) {
+                sanitizers.put(id, sanitizer);
+            }
+        }
+        return sanitizers;
+    }
+
+    @NonNull
+    private AutofillValue getSanitizedValue(
+            @Nullable ArrayMap<AutofillId, InternalSanitizer> sanitizers,
+            @NonNull AutofillId id,
+            @NonNull AutofillValue value) {
+        if (sanitizers == null) return value;
+
+        final InternalSanitizer sanitizer = sanitizers.get(id);
+        if (sanitizer == null) {
+            return value;
+        }
+
+        final AutofillValue sanitized = sanitizer.sanitize(value);
+        if (sDebug) Slog.d(TAG, "Value for " + id + "(" + value + ") sanitized to " + sanitized);
+        return sanitized;
+    }
+
     /**
      * Returns whether the session is currently showing the save UI
      */
@@ -1100,6 +1147,9 @@
             return;
         }
 
+        final ArrayMap<AutofillId, InternalSanitizer> sanitizers =
+                createSanitizers(getSaveInfoLocked());
+
         final int numContexts = mContexts.size();
 
         for (int contextNum = 0; contextNum < numContexts; contextNum++) {
@@ -1126,7 +1176,9 @@
                 }
                 if (sVerbose) Slog.v(TAG, "callSaveLocked(): updating " + id + " to " + value);
 
-                node.updateAutofillValue(value);
+                final AutofillValue sanitizedValue = getSanitizedValue(sanitizers, id, value);
+
+                node.updateAutofillValue(sanitizedValue);
             }
 
             // Sanitize structure before it's sent to service.
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index f4dbb5a..eabe21f 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -401,45 +401,53 @@
         @Override
         public void onUnlockUser(int userId) {
             if (userId == UserHandle.USER_SYSTEM) {
-                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
-                sInstance.initialize(userId);
-                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
-                // Migrate legacy setting
-                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
-                if (!backupSettingMigrated(userId)) {
-                    if (DEBUG) {
-                        Slog.i(TAG, "Backup enable apparently not migrated");
-                    }
-                    final ContentResolver r = sInstance.mContext.getContentResolver();
-                    final int enableState = Settings.Secure.getIntForUser(r,
-                            Settings.Secure.BACKUP_ENABLED, -1, userId);
-                    if (enableState >= 0) {
-                        if (DEBUG) {
-                            Slog.i(TAG, "Migrating enable state " + (enableState != 0));
-                        }
-                        writeBackupEnableState(enableState != 0, userId);
-                        Settings.Secure.putStringForUser(r,
-                                Settings.Secure.BACKUP_ENABLED, null, userId);
-                    } else {
-                        if (DEBUG) {
-                            Slog.i(TAG, "Backup not yet configured; retaining null enable state");
-                        }
-                    }
-                }
-                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
-                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
-                try {
-                    sInstance.setBackupEnabled(readBackupEnableState(userId));
-                } catch (RemoteException e) {
-                    // can't happen; it's a local object
-                }
-                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                sInstance.unlockSystemUser();
             }
         }
     }
 
+    // Called through the trampoline from onUnlockUser(), then we buck the work
+    // off to the background thread to keep the unlock time down.
+    public void unlockSystemUser() {
+        mBackupHandler.post(() -> {
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
+            sInstance.initialize(UserHandle.USER_SYSTEM);
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+            // Migrate legacy setting
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
+            if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) {
+                if (DEBUG) {
+                    Slog.i(TAG, "Backup enable apparently not migrated");
+                }
+                final ContentResolver r = sInstance.mContext.getContentResolver();
+                final int enableState = Settings.Secure.getIntForUser(r,
+                        Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM);
+                if (enableState >= 0) {
+                    if (DEBUG) {
+                        Slog.i(TAG, "Migrating enable state " + (enableState != 0));
+                    }
+                    writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM);
+                    Settings.Secure.putStringForUser(r,
+                            Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM);
+                } else {
+                    if (DEBUG) {
+                        Slog.i(TAG, "Backup not yet configured; retaining null enable state");
+                    }
+                }
+            }
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
+            try {
+                sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
+            } catch (RemoteException e) {
+                // can't happen; it's a local object
+            }
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        });
+    }
+
     class ProvisionedObserver extends ContentObserver {
         public ProvisionedObserver(Handler handler) {
             super(handler);
diff --git a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
index 5dfa630..041f9ed 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
@@ -39,6 +39,8 @@
  */
 public interface BackupManagerServiceInterface {
 
+  void unlockSystemUser();
+
   // Utility: build a new random integer token
   int generateRandomIntegerToken();
 
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index fe4902f..f298065 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -548,45 +548,53 @@
         @Override
         public void onUnlockUser(int userId) {
             if (userId == UserHandle.USER_SYSTEM) {
-                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
-                sInstance.initialize(userId);
-                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
-                // Migrate legacy setting
-                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
-                if (!backupSettingMigrated(userId)) {
-                    if (DEBUG) {
-                        Slog.i(TAG, "Backup enable apparently not migrated");
-                    }
-                    final ContentResolver r = sInstance.mContext.getContentResolver();
-                    final int enableState = Settings.Secure.getIntForUser(r,
-                            Settings.Secure.BACKUP_ENABLED, -1, userId);
-                    if (enableState >= 0) {
-                        if (DEBUG) {
-                            Slog.i(TAG, "Migrating enable state " + (enableState != 0));
-                        }
-                        writeBackupEnableState(enableState != 0, userId);
-                        Settings.Secure.putStringForUser(r,
-                                Settings.Secure.BACKUP_ENABLED, null, userId);
-                    } else {
-                        if (DEBUG) {
-                            Slog.i(TAG, "Backup not yet configured; retaining null enable state");
-                        }
-                    }
-                }
-                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
-                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
-                try {
-                    sInstance.setBackupEnabled(readBackupEnableState(userId));
-                } catch (RemoteException e) {
-                    // can't happen; it's a local object
-                }
-                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                sInstance.unlockSystemUser();
             }
         }
     }
 
+    // Called through the trampoline from onUnlockUser(), then we buck the work
+    // off to the background thread to keep the unlock time down.
+    public void unlockSystemUser() {
+        mBackupHandler.post(() -> {
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
+            sInstance.initialize(UserHandle.USER_SYSTEM);
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+            // Migrate legacy setting
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
+            if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) {
+                if (DEBUG) {
+                    Slog.i(TAG, "Backup enable apparently not migrated");
+                }
+                final ContentResolver r = sInstance.mContext.getContentResolver();
+                final int enableState = Settings.Secure.getIntForUser(r,
+                        Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM);
+                if (enableState >= 0) {
+                    if (DEBUG) {
+                        Slog.i(TAG, "Migrating enable state " + (enableState != 0));
+                    }
+                    writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM);
+                    Settings.Secure.putStringForUser(r,
+                            Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM);
+                } else {
+                    if (DEBUG) {
+                        Slog.i(TAG, "Backup not yet configured; retaining null enable state");
+                    }
+                }
+            }
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
+            try {
+                sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
+            } catch (RemoteException e) {
+                // can't happen; it's a local object
+            }
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        });
+    }
+
     // Bookkeeping of in-flight operations for timeout etc. purposes.  The operation
     // token is the index of the entry in the pending-operations list.
     public static final int OP_PENDING = 0;
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 245bc1d..9739e38 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -139,6 +139,13 @@
         }
     }
 
+    void unlockSystemUser() {
+        BackupManagerServiceInterface svc = mService;
+        if (svc != null) {
+            svc.unlockSystemUser();
+        }
+    }
+
     public void setBackupServiceActive(final int userHandle, boolean makeActive) {
         // Only the DPM should be changing the active state of backup
         final int caller = binderGetCallingUid();
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index abbc89e..2d9baf6 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -307,6 +307,12 @@
      */
     private int[] mTempWhitelistAppIdArray = new int[0];
 
+    /**
+     * Apps in the system whitelist that have been taken out (probably because the user wanted to).
+     * They can be restored back by calling restoreAppToSystemWhitelist(String).
+     */
+    private ArrayMap<String, Integer> mRemovedFromSystemWhitelistApps = new ArrayMap<>();
+
     private static final int EVENT_NULL = 0;
     private static final int EVENT_NORMAL = 1;
     private static final int EVENT_LIGHT_IDLE = 2;
@@ -760,17 +766,15 @@
         public long NOTIFICATION_WHITELIST_DURATION;
 
         private final ContentResolver mResolver;
-        private final boolean mHasWatch;
+        private final boolean mSmallBatteryDevice;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
 
         public Constants(Handler handler, ContentResolver resolver) {
             super(handler);
             mResolver = resolver;
-            mHasWatch = getContext().getPackageManager().hasSystemFeature(
-                    PackageManager.FEATURE_WATCH);
-            mResolver.registerContentObserver(Settings.Global.getUriFor(
-                    mHasWatch ? Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH
-                              : Settings.Global.DEVICE_IDLE_CONSTANTS),
+            mSmallBatteryDevice = ActivityManager.isSmallBatteryDevice();
+            mResolver.registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.DEVICE_IDLE_CONSTANTS),
                     false, this);
             updateConstants();
         }
@@ -784,8 +788,7 @@
             synchronized (DeviceIdleController.this) {
                 try {
                     mParser.setString(Settings.Global.getString(mResolver,
-                            mHasWatch ? Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH
-                                      : Settings.Global.DEVICE_IDLE_CONSTANTS));
+                            Settings.Global.DEVICE_IDLE_CONSTANTS));
                 } catch (IllegalArgumentException e) {
                     // Failed to parse the settings string, log this and move on
                     // with defaults.
@@ -815,7 +818,7 @@
                 MIN_DEEP_MAINTENANCE_TIME = mParser.getLong(
                         KEY_MIN_DEEP_MAINTENANCE_TIME,
                         !COMPRESS_TIME ? 30 * 1000L : 5 * 1000L);
-                long inactiveTimeoutDefault = (mHasWatch ? 15 : 30) * 60 * 1000L;
+                long inactiveTimeoutDefault = (mSmallBatteryDevice ? 15 : 30) * 60 * 1000L;
                 INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT,
                         !COMPRESS_TIME ? inactiveTimeoutDefault : (inactiveTimeoutDefault / 10));
                 SENSING_TIMEOUT = mParser.getLong(KEY_SENSING_TIMEOUT,
@@ -825,7 +828,7 @@
                 LOCATION_ACCURACY = mParser.getFloat(KEY_LOCATION_ACCURACY, 20);
                 MOTION_INACTIVE_TIMEOUT = mParser.getLong(KEY_MOTION_INACTIVE_TIMEOUT,
                         !COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L);
-                long idleAfterInactiveTimeout = (mHasWatch ? 15 : 30) * 60 * 1000L;
+                long idleAfterInactiveTimeout = (mSmallBatteryDevice ? 15 : 30) * 60 * 1000L;
                 IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong(KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
                         !COMPRESS_TIME ? idleAfterInactiveTimeout
                                        : (idleAfterInactiveTimeout / 10));
@@ -1162,6 +1165,38 @@
             }
         }
 
+        @Override public void removeSystemPowerWhitelistApp(String name) {
+            if (DEBUG) {
+                Slog.d(TAG, "removeAppFromSystemWhitelist(name = " + name + ")");
+            }
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+                    null);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                removeSystemPowerWhitelistAppInternal(name);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override public void restoreSystemPowerWhitelistApp(String name) {
+            if (DEBUG) {
+                Slog.d(TAG, "restoreAppToSystemWhitelist(name = " + name + ")");
+            }
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+                    null);
+            long ident = Binder.clearCallingIdentity();
+            try {
+                restoreSystemPowerWhitelistAppInternal(name);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        public String[] getRemovedSystemPowerWhitelistApps() {
+            return getRemovedSystemPowerWhitelistAppsInternal();
+        }
+
         @Override public String[] getSystemPowerWhitelistExceptIdle() {
             return getSystemPowerWhitelistExceptIdleInternal();
         }
@@ -1504,6 +1539,42 @@
         }
     }
 
+    void resetSystemPowerWhitelistInternal() {
+        synchronized (this) {
+            mPowerSaveWhitelistApps.putAll(mRemovedFromSystemWhitelistApps);
+            mRemovedFromSystemWhitelistApps.clear();
+            reportPowerSaveWhitelistChangedLocked();
+            updateWhitelistAppIdsLocked();
+            writeConfigFileLocked();
+        }
+    }
+
+    public boolean restoreSystemPowerWhitelistAppInternal(String name) {
+        synchronized (this) {
+            if (!mRemovedFromSystemWhitelistApps.containsKey(name)) {
+                return false;
+            }
+            mPowerSaveWhitelistApps.put(name, mRemovedFromSystemWhitelistApps.remove(name));
+            reportPowerSaveWhitelistChangedLocked();
+            updateWhitelistAppIdsLocked();
+            writeConfigFileLocked();
+            return true;
+        }
+    }
+
+    public boolean removeSystemPowerWhitelistAppInternal(String name) {
+        synchronized (this) {
+            if (!mPowerSaveWhitelistApps.containsKey(name)) {
+                return false;
+            }
+            mRemovedFromSystemWhitelistApps.put(name, mPowerSaveWhitelistApps.remove(name));
+            reportPowerSaveWhitelistChangedLocked();
+            updateWhitelistAppIdsLocked();
+            writeConfigFileLocked();
+            return true;
+        }
+    }
+
     public boolean addPowerSaveWhitelistExceptIdleInternal(String name) {
         synchronized (this) {
             try {
@@ -1565,6 +1636,17 @@
         }
     }
 
+    public String[] getRemovedSystemPowerWhitelistAppsInternal() {
+        synchronized (this) {
+            int size = mRemovedFromSystemWhitelistApps.size();
+            final String[] apps = new String[size];
+            for (int i = 0; i < size; i++) {
+                apps[i] = mRemovedFromSystemWhitelistApps.keyAt(i);
+            }
+            return apps;
+        }
+    }
+
     public String[] getUserPowerWhitelistInternal() {
         synchronized (this) {
             int size = mPowerSaveWhitelistUserApps.size();
@@ -2481,21 +2563,31 @@
                 }
 
                 String tagName = parser.getName();
-                if (tagName.equals("wl")) {
-                    String name = parser.getAttributeValue(null, "n");
-                    if (name != null) {
-                        try {
-                            ApplicationInfo ai = pm.getApplicationInfo(name,
-                                    PackageManager.MATCH_ANY_USER);
-                            mPowerSaveWhitelistUserApps.put(ai.packageName,
-                                    UserHandle.getAppId(ai.uid));
-                        } catch (PackageManager.NameNotFoundException e) {
+                switch (tagName) {
+                    case "wl":
+                        String name = parser.getAttributeValue(null, "n");
+                        if (name != null) {
+                            try {
+                                ApplicationInfo ai = pm.getApplicationInfo(name,
+                                        PackageManager.MATCH_ANY_USER);
+                                mPowerSaveWhitelistUserApps.put(ai.packageName,
+                                        UserHandle.getAppId(ai.uid));
+                            } catch (PackageManager.NameNotFoundException e) {
+                            }
                         }
-                    }
-                } else {
-                    Slog.w(TAG, "Unknown element under <config>: "
-                            + parser.getName());
-                    XmlUtils.skipCurrentTag(parser);
+                        break;
+                    case "un-wl":
+                        final String packageName = parser.getAttributeValue(null, "n");
+                        if (mPowerSaveWhitelistApps.containsKey(packageName)) {
+                            mRemovedFromSystemWhitelistApps.put(packageName,
+                                    mPowerSaveWhitelistApps.remove(packageName));
+                        }
+                        break;
+                    default:
+                        Slog.w(TAG, "Unknown element under <config>: "
+                                + parser.getName());
+                        XmlUtils.skipCurrentTag(parser);
+                        break;
                 }
             }
 
@@ -2556,6 +2648,11 @@
             out.attribute(null, "n", name);
             out.endTag(null, "wl");
         }
+        for (int i = 0; i < mRemovedFromSystemWhitelistApps.size(); i++) {
+            out.startTag(null, "un-wl");
+            out.attribute(null, "n", mRemovedFromSystemWhitelistApps.keyAt(i));
+            out.endTag(null, "un-wl");
+        }
         out.endTag(null, "config");
         out.endDocument();
     }
@@ -2584,6 +2681,13 @@
         pw.println("    Print currently whitelisted apps.");
         pw.println("  whitelist [package ...]");
         pw.println("    Add (prefix with +) or remove (prefix with -) packages.");
+        pw.println("  sys-whitelist [package ...|reset]");
+        pw.println("    Prefix the package with '-' to remove it from the system whitelist or '+'"
+                + " to put it back in the system whitelist.");
+        pw.println("    Note that only packages that were"
+                + " earlier removed from the system whitelist can be added back.");
+        pw.println("    reset will reset the whitelist to the original state");
+        pw.println("    Prints the system whitelist if no arguments are specified");
         pw.println("  except-idle-whitelist [package ...|reset]");
         pw.println("    Prefix the package with '+' to add it to whitelist or "
                 + "'=' to check if it is already whitelisted");
@@ -2944,6 +3048,50 @@
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
+        } else if ("sys-whitelist".equals(cmd)) {
+            String arg = shell.getNextArg();
+            if (arg != null) {
+                getContext().enforceCallingOrSelfPermission(
+                        android.Manifest.permission.DEVICE_POWER, null);
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    if ("reset".equals(arg)) {
+                        resetSystemPowerWhitelistInternal();
+                    } else {
+                        do {
+                            if (arg.length() < 1
+                                    || (arg.charAt(0) != '-' && arg.charAt(0) != '+')) {
+                                pw.println("Package must be prefixed with + or - " + arg);
+                                return -1;
+                            }
+                            final char op = arg.charAt(0);
+                            final String pkg = arg.substring(1);
+                            switch (op) {
+                                case '+':
+                                    if (restoreSystemPowerWhitelistAppInternal(pkg)) {
+                                        pw.println("Restored " + pkg);
+                                    }
+                                    break;
+                                case '-':
+                                    if (removeSystemPowerWhitelistAppInternal(pkg)) {
+                                        pw.println("Removed " + pkg);
+                                    }
+                                    break;
+                            }
+                        } while ((arg = shell.getNextArg()) != null);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            } else {
+                synchronized (this) {
+                    for (int j=0; j<mPowerSaveWhitelistApps.size(); j++) {
+                        pw.print(mPowerSaveWhitelistApps.keyAt(j));
+                        pw.print(",");
+                        pw.println(mPowerSaveWhitelistApps.valueAt(j));
+                    }
+                }
+            }
         } else {
             return shell.handleDefaultCommands(cmd);
         }
@@ -3027,6 +3175,14 @@
                     pw.println(mPowerSaveWhitelistApps.keyAt(i));
                 }
             }
+            size = mRemovedFromSystemWhitelistApps.size();
+            if (size > 0) {
+                pw.println("  Removed from whitelist system apps:");
+                for (int i = 0; i < size; i++) {
+                    pw.print("    ");
+                    pw.println(mRemovedFromSystemWhitelistApps.keyAt(i));
+                }
+            }
             size = mPowerSaveWhitelistUserApps.size();
             if (size > 0) {
                 pw.println("  Whitelist user apps:");
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 046eb76..8b79b9d 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -373,12 +373,24 @@
             if (mCurrentVibration.hasLongerTimeout(newOneShot.getTiming())
                     && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
                 if (DEBUG) {
-                    Slog.e(TAG, "Ignoring incoming vibration in favor of current vibration");
+                    Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration");
                 }
                 return;
             }
         }
 
+        // If the current vibration is repeating and the incoming one is non-repeating, then ignore
+        // the non-repeating vibration. This is so that we don't cancel vibrations that are meant
+        // to grab the attention of the user, like ringtones and alarms, in favor of one-shot
+        // vibrations that are likely quite short.
+        if (!isRepeatingVibration(effect)
+                && mCurrentVibration != null && isRepeatingVibration(mCurrentVibration.mEffect)) {
+            if (DEBUG) {
+                Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
+            }
+            return;
+        }
+
         Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
 
         // Only link against waveforms since they potentially don't have a finish if
@@ -404,6 +416,16 @@
         }
     }
 
+    private static boolean isRepeatingVibration(VibrationEffect effect) {
+        if (effect instanceof VibrationEffect.Waveform) {
+            final VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
+            if (waveform.getRepeatIndex() >= 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void addToPreviousVibrationsLocked(Vibration vib) {
         if (mPreviousVibrations.size() > mPreviousVibrationsLimit) {
             mPreviousVibrations.removeFirst();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8ec33d7..0b2dd9b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -38,6 +38,7 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
@@ -1718,6 +1719,7 @@
     static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
     static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
     static final int TOP_APP_KILLED_BY_LMK_MSG = 73;
+    static final int NOTIFY_VR_KEYGUARD_MSG = 74;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -2385,6 +2387,9 @@
             case NOTIFY_VR_SLEEPING_MSG: {
                 notifyVrManagerOfSleepState(msg.arg1 != 0);
             } break;
+            case NOTIFY_VR_KEYGUARD_MSG: {
+                notifyVrManagerOfKeyguardState(msg.arg1 != 0);
+            } break;
             case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
                 synchronized (ActivityManagerService.this) {
                     for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -3240,7 +3245,8 @@
         if (r.requestedVrComponent != null && r.getStackId() >= FIRST_DYNAMIC_STACK_ID) {
             Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId()
                     + " to main stack for VR");
-            moveTaskToStack(r.getTask().taskId, FULLSCREEN_WORKSPACE_STACK_ID, true /* toTop */);
+            setTaskWindowingMode(r.getTask().taskId,
+                    WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */);
         }
         mHandler.sendMessage(
                 mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
@@ -3259,6 +3265,19 @@
         vrService.onSleepStateChanged(isSleeping);
     }
 
+    private void sendNotifyVrManagerOfKeyguardState(boolean isShowing) {
+        mHandler.sendMessage(
+                mHandler.obtainMessage(NOTIFY_VR_KEYGUARD_MSG, isShowing ? 1 : 0, 0));
+    }
+
+    private void notifyVrManagerOfKeyguardState(boolean isShowing) {
+        final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+        if (vrService == null) {
+            return;
+        }
+        vrService.onKeyguardStateChanged(isShowing);
+    }
+
     final void showAskCompatModeDialogLocked(ActivityRecord r) {
         Message msg = Message.obtain();
         msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
@@ -9861,6 +9880,7 @@
         }
         rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode();
         rti.resizeMode = tr.mResizeMode;
+        rti.configuration.setTo(tr.getConfiguration());
 
         ActivityRecord base = null;
         ActivityRecord top = null;
@@ -10609,6 +10629,42 @@
     }
 
     @Override
+    public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
+        synchronized (this) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+                if (task == null) {
+                    Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId);
+                    return;
+                }
+
+                if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingMode: moving task=" + taskId
+                        + " to windowingMode=" + windowingMode + " toTop=" + toTop);
+                if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+                    mWindowManager.setDockedStackCreateState(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
+                            null /* initialBounds */);
+                }
+
+                if (!task.isActivityTypeStandardOrUndefined()) {
+                    throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move task "
+                            + taskId + " to non-standard windowin mode=" + windowingMode);
+                }
+                final ActivityDisplay display = task.getStack().getDisplay();
+                final ActivityStack stack = display.getOrCreateStack(windowingMode,
+                        task.getStack().getActivityType(), toTop);
+                // TODO: We should just change the windowing mode for the task vs. creating and
+                // moving it to a stack.
+                task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
+                        "moveTaskToStack");
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
     public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
         enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
         synchronized (this) {
@@ -10702,6 +10758,66 @@
     }
 
     /**
+     * Dismisses split-screen multi-window mode.
+     * @param toTop If true the current primary split-screen stack will be placed or left on top.
+     */
+    @Override
+    public void dismissSplitScreenMode(boolean toTop) {
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                final ActivityStack stack =
+                        mStackSupervisor.getDefaultDisplay().getSplitScreenStack();
+                if (toTop) {
+                    mStackSupervisor.resizeStackLocked(stack.mStackId, null /* destBounds */,
+                            null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+                            true /* preserveWindows */, true /* allowResizeInDockedMode */,
+                            !DEFER_RESUME);
+                } else {
+                    mStackSupervisor.moveTasksToFullscreenStackLocked(stack, false /* onTop */);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * Dismisses Pip
+     * @param animate True if the dismissal should be animated.
+     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
+     *                          default animation duration should be used.
+     */
+    @Override
+    public void dismissPip(boolean animate, int animationDuration) {
+        enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                final PinnedActivityStack stack =
+                        mStackSupervisor.getDefaultDisplay().getPinnedStack();
+
+                if (stack == null) {
+                    return;
+                }
+                if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
+                    throw new IllegalArgumentException("Stack: " + stack
+                            + " doesn't support animated resize.");
+                }
+                if (animate) {
+                    stack.animateResizePinnedStack(null /* sourceHintBounds */,
+                            null /* destBounds */, animationDuration, false /* fromFullscreen */);
+                } else {
+                    mStackSupervisor.moveTasksToFullscreenStackLocked(stack, true /* onTop */);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
      * Moves the top activity in the input stackId to the pinned stack.
      *
      * @param stackId Id of stack to move the top activity to pinned stack.
@@ -12550,6 +12666,7 @@
                 Binder.restoreCallingIdentity(ident);
             }
         }
+        sendNotifyVrManagerOfKeyguardState(showing);
     }
 
     @Override
@@ -14580,6 +14697,9 @@
                 }
                 sb.append("\n");
             }
+            if (process.info.isInstantApp()) {
+                sb.append("Instant-App: true\n");
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 34eaab2..1940ca2 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -4900,7 +4900,7 @@
             ci.numRunning = numRunning;
             ci.supportsSplitScreenMultiWindow = task.supportsSplitScreenWindowingMode();
             ci.resizeMode = task.mResizeMode;
-            ci.configuration = task.getConfiguration();
+            ci.configuration.setTo(task.getConfiguration());
             list.add(ci);
         }
     }
@@ -5089,7 +5089,7 @@
             if (isAttached()) {
                 getDisplay().positionChildAtBottom(this);
             }
-            if (!isHomeOrRecentsStack()) {
+            if (!isActivityTypeHome()) {
                 remove();
             }
         }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 42f3550..da2827a 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2625,38 +2625,41 @@
             // the picture-in-picture mode.
             final boolean schedulePictureInPictureModeChange = inPinnedWindowingMode;
             final ArrayList<TaskRecord> tasks = fromStack.getAllTasks();
-            final int size = tasks.size();
-            final ActivityStack fullscreenStack = toDisplay.getOrCreateStack(
-                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, onTop);
 
-            if (onTop) {
-                final int returnToType =
-                        toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED);
-                for (int i = 0; i < size; i++) {
-                    final TaskRecord task = tasks.get(i);
-                    final boolean isTopTask = i == (size - 1);
-                    if (inPinnedWindowingMode) {
-                        // Update the return-to to reflect where the pinned stack task was moved
-                        // from so that we retain the stack that was previously visible if the
-                        // pinned stack is recreated. See moveActivityToPinnedStackLocked().
-                        task.setTaskToReturnTo(returnToType);
+            if (!tasks.isEmpty()) {
+                final int size = tasks.size();
+                final ActivityStack fullscreenStack = toDisplay.getOrCreateStack(
+                        WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, onTop);
+
+                if (onTop) {
+                    final int returnToType =
+                            toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED);
+                    for (int i = 0; i < size; i++) {
+                        final TaskRecord task = tasks.get(i);
+                        final boolean isTopTask = i == (size - 1);
+                        if (inPinnedWindowingMode) {
+                            // Update the return-to to reflect where the pinned stack task was moved
+                            // from so that we retain the stack that was previously visible if the
+                            // pinned stack is recreated. See moveActivityToPinnedStackLocked().
+                            task.setTaskToReturnTo(returnToType);
+                        }
+                        // Defer resume until all the tasks have been moved to the fullscreen stack
+                        task.reparent(fullscreenStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
+                                isTopTask /* animate */, DEFER_RESUME,
+                                schedulePictureInPictureModeChange,
+                                "moveTasksToFullscreenStack - onTop");
                     }
-                    // Defer resume until all the tasks have been moved to the fullscreen stack
-                    task.reparent(fullscreenStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
-                            isTopTask /* animate */, DEFER_RESUME,
-                            schedulePictureInPictureModeChange,
-                            "moveTasksToFullscreenStack - onTop");
-                }
-            } else {
-                for (int i = 0; i < size; i++) {
-                    final TaskRecord task = tasks.get(i);
-                    // Position the tasks in the fullscreen stack in order at the bottom of the
-                    // stack. Also defer resume until all the tasks have been moved to the
-                    // fullscreen stack.
-                    task.reparent(fullscreenStack, i /* position */,
-                            REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
-                            schedulePictureInPictureModeChange,
-                            "moveTasksToFullscreenStack - NOT_onTop");
+                } else {
+                    for (int i = 0; i < size; i++) {
+                        final TaskRecord task = tasks.get(i);
+                        // Position the tasks in the fullscreen stack in order at the bottom of the
+                        // stack. Also defer resume until all the tasks have been moved to the
+                        // fullscreen stack.
+                        task.reparent(fullscreenStack, i /* position */,
+                                REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
+                                schedulePictureInPictureModeChange,
+                                "moveTasksToFullscreenStack - NOT_onTop");
+                    }
                 }
             }
 
@@ -3216,8 +3219,7 @@
         ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         resumeFocusedStackTopActivityLocked();
 
-        mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName, r.userId,
-                r.getTask().taskId);
+        mService.mTaskChangeNotificationController.notifyActivityPinned(r);
     }
 
     /** Move activity with its stack to front and make the stack focused. */
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index ed33d58..d444c66 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -186,6 +186,12 @@
     private boolean mAvoidMoveToFront;
     private boolean mPowerHintSent;
 
+    // We must track when we deliver the new intent since multiple code paths invoke
+    // {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used
+    // inside {@link #deliverNewIntent} to suppress duplicate requests and ensure the intent is
+    // delivered at most once.
+    private boolean mIntentDelivered;
+
     private IVoiceInteractionSession mVoiceSession;
     private IVoiceInteractor mVoiceInteractor;
 
@@ -243,6 +249,8 @@
         mVoiceInteractor = null;
 
         mUsingVr2dDisplay = false;
+
+        mIntentDelivered = false;
     }
 
     ActivityStarter(ActivityManagerService service, ActivityStackSupervisor supervisor) {
@@ -1077,9 +1085,7 @@
                         // so make sure the task now has the identity of the new intent.
                         top.getTask().setIntent(mStartActivity);
                     }
-                    ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
-                    top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
-                            mStartActivity.launchedFromPackage);
+                    deliverNewIntent(top);
                 }
             }
 
@@ -1141,7 +1147,6 @@
                 && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
                 || mLaunchSingleTop || mLaunchSingleTask);
         if (dontStart) {
-            ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask());
             // For paranoia, make sure we have correctly resumed the top activity.
             topStack.mLastPausedActivity = null;
             if (mDoResume) {
@@ -1153,8 +1158,8 @@
                 // anything if that is the case, so this is it!
                 return START_RETURN_INTENT_TO_CALLER;
             }
-            top.deliverNewIntentLocked(
-                    mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+
+            deliverNewIntent(top);
 
             // Don't use mStartActivity.task to show the toast. We're not starting a new activity
             // but reusing 'top'. Fields in mStartActivity may not be fully initialized.
@@ -1745,13 +1750,10 @@
             // desires.
             if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop)
                     && intentActivity.realActivity.equals(mStartActivity.realActivity)) {
-                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity,
-                        intentActivity.getTask());
                 if (intentActivity.frontOfTask) {
                     intentActivity.getTask().setIntent(mStartActivity);
                 }
-                intentActivity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
-                        mStartActivity.launchedFromPackage);
+                deliverNewIntent(intentActivity);
             } else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) {
                 // In this case we are launching the root activity of the task, but with a
                 // different intent. We should start a new instance on top.
@@ -1831,6 +1833,17 @@
         return START_SUCCESS;
     }
 
+    private void deliverNewIntent(ActivityRecord activity) {
+        if (mIntentDelivered) {
+            return;
+        }
+
+        ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTask());
+        activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
+                mStartActivity.launchedFromPackage);
+        mIntentDelivered = true;
+    }
+
     private int setTaskFromSourceRecord() {
         if (mService.mLockTaskController.isLockTaskModeViolation(mSourceRecord.getTask())) {
             Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
@@ -1887,7 +1900,7 @@
             mKeepCurTransition = true;
             if (top != null) {
                 ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
-                top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+                deliverNewIntent(top);
                 // For paranoia, make sure we have correctly resumed the top activity.
                 mTargetStack.mLastPausedActivity = null;
                 if (mDoResume) {
@@ -1906,7 +1919,7 @@
                 task.moveActivityToFrontLocked(top);
                 top.updateOptionsLocked(mOptions);
                 ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task);
-                top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+                deliverNewIntent(top);
                 mTargetStack.mLastPausedActivity = null;
                 if (mDoResume) {
                     mSupervisor.resumeFocusedStackTopActivityLocked();
@@ -1942,14 +1955,12 @@
                     || mLaunchSingleTop || mLaunchSingleTask) {
                 mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions,
                         mStartActivity.appTimeTracker, "inTaskToFront");
-                ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask());
                 if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
                     // We don't need to start a new activity, and the client said not to do
                     // anything if that is the case, so this is it!
                     return START_RETURN_INTENT_TO_CALLER;
                 }
-                top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
-                        mStartActivity.launchedFromPackage);
+                deliverNewIntent(top);
                 return START_DELIVERED_TO_TOP;
             }
         }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index e839003..7c9cd00 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -220,7 +220,7 @@
         // Shutdown the thread we made.
         mWorker.shutdown();
     }
-    
+
     public static IBatteryStats getService() {
         if (sService != null) {
             return sService;
@@ -611,21 +611,21 @@
             mStats.notePhoneOnLocked();
         }
     }
-    
+
     public void notePhoneOff() {
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.notePhoneOffLocked();
         }
     }
-    
+
     public void notePhoneSignalStrength(SignalStrength signalStrength) {
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.notePhoneSignalStrengthLocked(signalStrength);
         }
     }
-    
+
     public void notePhoneDataConnectionState(int dataType, boolean hasData) {
         enforceCallingPermission();
         synchronized (mStats) {
@@ -647,7 +647,7 @@
             mStats.noteWifiOnLocked();
         }
     }
-    
+
     public void noteWifiOff() {
         enforceCallingPermission();
         synchronized (mStats) {
@@ -806,7 +806,7 @@
             mStats.noteFullWifiLockAcquiredLocked(uid);
         }
     }
-    
+
     public void noteFullWifiLockReleased(int uid) {
         enforceCallingPermission();
         synchronized (mStats) {
@@ -1043,7 +1043,7 @@
             });
         });
     }
-    
+
     public long getAwakeTimeBattery() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BATTERY_STATS, null);
@@ -1186,6 +1186,7 @@
 
         int flags = 0;
         boolean useCheckinFormat = false;
+        boolean toProto = false;
         boolean isRealCheckin = false;
         boolean noOutput = false;
         boolean writeData = false;
@@ -1212,6 +1213,8 @@
                 } else if ("-c".equals(arg)) {
                     useCheckinFormat = true;
                     flags |= BatteryStats.DUMP_INCLUDE_HISTORY;
+                } else if ("--proto".equals(arg)) {
+                    toProto = true;
                 } else if ("--charged".equals(arg)) {
                     flags |= BatteryStats.DUMP_CHARGED_ONLY;
                 } else if ("--daily".equals(arg)) {
@@ -1304,7 +1307,45 @@
             }
         }
 
-        if (useCheckinFormat) {
+        if (toProto) {
+            List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
+                    PackageManager.MATCH_ANY_USER | PackageManager.MATCH_ALL);
+            if (isRealCheckin) {
+                // For a real checkin, first we want to prefer to use the last complete checkin
+                // file if there is one.
+                synchronized (mStats.mCheckinFile) {
+                    if (mStats.mCheckinFile.exists()) {
+                        try {
+                            byte[] raw = mStats.mCheckinFile.readFully();
+                            if (raw != null) {
+                                Parcel in = Parcel.obtain();
+                                in.unmarshall(raw, 0, raw.length);
+                                in.setDataPosition(0);
+                                BatteryStatsImpl checkinStats = new BatteryStatsImpl(
+                                        null, mStats.mHandler, null, mUserManagerUserInfoProvider);
+                                checkinStats.readSummaryFromParcel(in);
+                                in.recycle();
+                                checkinStats.dumpProtoLocked(mContext, fd, apps, flags,
+                                        historyStart);
+                                mStats.mCheckinFile.delete();
+                                return;
+                            }
+                        } catch (IOException | ParcelFormatException e) {
+                            Slog.w(TAG, "Failure reading checkin file "
+                                    + mStats.mCheckinFile.getBaseFile(), e);
+                        }
+                    }
+                }
+            }
+            if (DBG) Slog.d(TAG, "begin dumpProtoLocked from UID " + Binder.getCallingUid());
+            synchronized (mStats) {
+                mStats.dumpProtoLocked(mContext, fd, apps, flags, historyStart);
+                if (writeData) {
+                    mStats.writeAsyncLocked();
+                }
+            }
+            if (DBG) Slog.d(TAG, "end dumpProtoLocked");
+        } else if (useCheckinFormat) {
             List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
                     PackageManager.MATCH_ANY_USER | PackageManager.MATCH_ALL);
             if (isRealCheckin) {
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
index 6a986bb..5a7e7ce 100644
--- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
@@ -95,7 +95,8 @@
     };
 
     private final TaskStackConsumer mNotifyActivityPinned = (l, m) -> {
-        l.onActivityPinned((String) m.obj, m.arg1, m.arg2);
+        final ActivityRecord r = (ActivityRecord) m.obj;
+        l.onActivityPinned(r.packageName, r.userId, r.getTask().taskId, r.getStackId());
     };
 
     private final TaskStackConsumer mNotifyActivityUnpinned = (l, m) -> {
@@ -278,10 +279,9 @@
     }
 
     /** Notifies all listeners when an Activity is pinned. */
-    void notifyActivityPinned(String packageName, int userId, int taskId) {
+    void notifyActivityPinned(ActivityRecord r) {
         mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
-        final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG,
-                userId, taskId, packageName);
+        final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG, r);
         forAllLocalListeners(mNotifyActivityPinned, msg);
         msg.sendToTarget();
     }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 055c9f6..5a29594 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -631,7 +631,6 @@
     void finishUserStopping(final int userId, final UserState uss) {
         // On to the next.
         final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN);
-        shutdownIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         // This is the result receiver for the final shutdown broadcast.
         final IIntentReceiver shutdownReceiver = new IIntentReceiver.Stub() {
             @Override
diff --git a/services/core/java/com/android/server/audio/AudioEventLogger.java b/services/core/java/com/android/server/audio/AudioEventLogger.java
index a2c0f76..9ebd75b 100644
--- a/services/core/java/com/android/server/audio/AudioEventLogger.java
+++ b/services/core/java/com/android/server/audio/AudioEventLogger.java
@@ -49,9 +49,9 @@
         }
 
         /**
-         * Causes the string message for the event to appear in the verbose logcat.
+         * Causes the string message for the event to appear in the logcat.
          * Here is an example of how to create a new event (a StringEvent), adding it to the logger
-         * (an instance of AudioEventLogger) while also making it show in the verbose logcat:
+         * (an instance of AudioEventLogger) while also making it show in the logcat:
          * <pre>
          *     myLogger.log(
          *         (new StringEvent("something for logcat and logger")).printLog(MyClass.TAG) );
@@ -60,7 +60,7 @@
          * @return the same instance of the event
          */
         public Event printLog(String tag) {
-            Log.v(tag, eventToString());
+            Log.i(tag, eventToString());
             return this;
         }
 
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 7d742ff..c5f563c7 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -89,6 +89,9 @@
         pw.println("\nMediaFocusControl dump time: "
                 + DateFormat.getTimeInstance().format(new Date()));
         dumpFocusStack(pw);
+        pw.println("\n");
+        // log
+        mEventLogger.dump(pw);
     }
 
     //=================================================================
@@ -120,6 +123,14 @@
     private final static Object mAudioFocusLock = new Object();
 
     /**
+     * Arbitrary maximum size of audio focus stack to prevent apps OOM'ing this process.
+     */
+    private static final int MAX_STACK_SIZE = 100;
+
+    private static final AudioEventLogger mEventLogger = new AudioEventLogger(50,
+            "focus commands as seen by MediaFocusControl");
+
+    /**
      * Discard the current audio focus owner.
      * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
      * focus), remove it from the stack, and clear the remote control display.
@@ -643,11 +654,14 @@
     protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
             IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
             int sdk) {
-        Log.i(TAG, " AudioFocus  requestAudioFocus() from uid/pid " + Binder.getCallingUid()
-                + "/" + Binder.getCallingPid()
-                + " clientId=" + clientId
-                + " req=" + focusChangeHint
-                + " flags=0x" + Integer.toHexString(flags));
+        mEventLogger.log((new AudioEventLogger.StringEvent(
+                "requestAudioFocus() from uid/pid " + Binder.getCallingUid()
+                    + "/" + Binder.getCallingPid()
+                    + " clientId=" + clientId + " callingPack=" + callingPackageName
+                    + " req=" + focusChangeHint
+                    + " flags=0x" + Integer.toHexString(flags)
+                    + " sdk=" + sdk))
+                .printLog(TAG));
         // we need a valid binder callback for clients
         if (!cb.pingBinder()) {
             Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
@@ -660,6 +674,11 @@
         }
 
         synchronized(mAudioFocusLock) {
+            if (mFocusStack.size() > MAX_STACK_SIZE) {
+                Log.e(TAG, "Max AudioFocus stack size reached, failing requestAudioFocus()");
+                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+            }
+
             boolean enteringRingOrCall = !mRingOrCallActive
                     & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
             if (enteringRingOrCall) { mRingOrCallActive = true; }
@@ -770,10 +789,12 @@
      * */
     protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa,
             String callingPackageName) {
-        // AudioAttributes are currently ignored, to be used for zones
-        Log.i(TAG, " AudioFocus  abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
-                + "/" + Binder.getCallingPid()
-                + " clientId=" + clientId);
+        // AudioAttributes are currently ignored, to be used for zones / a11y
+        mEventLogger.log((new AudioEventLogger.StringEvent(
+                "abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
+                    + "/" + Binder.getCallingPid()
+                    + " clientId=" + clientId))
+                .printLog(TAG));
         try {
             // this will take care of notifying the new focus owner if needed
             synchronized(mAudioFocusLock) {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index ef6de4c..fddb81b 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -98,6 +98,12 @@
     public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 9;
 
     /**
+     * Flag: This display will destroy its content on removal.
+     * @hide
+     */
+    public static final int FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 10;
+
+    /**
      * Touch attachment: Display does not receive touch.
      */
     public static final int TOUCH_NONE = 0;
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index addad0b..78a5407 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -238,6 +238,9 @@
                 // For private displays by default content is destroyed on removal.
                 mBaseDisplayInfo.removeMode = Display.REMOVE_MODE_DESTROY_CONTENT;
             }
+            if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) {
+                mBaseDisplayInfo.removeMode = Display.REMOVE_MODE_DESTROY_CONTENT;
+            }
             if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_PRESENTATION) != 0) {
                 mBaseDisplayInfo.flags |= Display.FLAG_PRESENTATION;
             }
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index d6ab888..f86d576 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -24,6 +24,8 @@
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
+import static android.hardware.display.DisplayManager
+        .VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
 
 import android.content.Context;
 import android.hardware.display.IVirtualDisplayCallback;
@@ -363,6 +365,9 @@
                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT) != 0) {
                     mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
                 }
+                if ((mFlags & VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) {
+                  mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL;
+                }
 
                 mInfo.type = Display.TYPE_VIRTUAL;
                 mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ?
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index b9a2d18..b102dde 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -205,9 +205,8 @@
     }
 
     private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
-        List<MediaSessionRecord> records;
+        List<MediaSessionRecord> records = new ArrayList<>();
         if (userId == UserHandle.USER_ALL) {
-            records = new ArrayList<>();
             int size = mUserRecords.size();
             for (int i = 0; i < size; i++) {
                 records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
@@ -216,9 +215,9 @@
             FullUserRecord user = getFullUserRecordLocked(userId);
             if (user == null) {
                 Log.w(TAG, "getSessions failed. Unknown user " + userId);
-                return new ArrayList<>();
+                return records;
             }
-            records = user.mPriorityStack.getActiveSessions(userId);
+            records.addAll(user.mPriorityStack.getActiveSessions(userId));
         }
 
         // Return global priority session at the first whenever it's asked.
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index c1d7059..019c7c2 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -546,6 +546,17 @@
         return null;
     }
 
+    protected boolean isServiceTokenValidLocked(IInterface service) {
+        if (service == null) {
+            return false;
+        }
+        ManagedServiceInfo info = getServiceFromTokenLocked(service);
+        if (info != null) {
+            return true;
+        }
+        return false;
+    }
+
     protected ManagedServiceInfo checkServiceTokenLocked(IInterface service) {
         checkNotNull(service);
         ManagedServiceInfo info = getServiceFromTokenLocked(service);
@@ -829,7 +840,12 @@
                     ServiceInfo info = mPm.getServiceInfo(component,
                             PackageManager.MATCH_DIRECT_BOOT_AWARE
                                     | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userIds[i]);
-                    if (info == null || !mConfig.bindPermission.equals(info.permission)) {
+                    if (info == null) {
+                        Slog.w(TAG, "Not binding " + getCaption() + " service " + component
+                                + ": service not found");
+                        continue;
+                    }
+                    if (!mConfig.bindPermission.equals(info.permission)) {
                         Slog.w(TAG, "Not binding " + getCaption() + " service " + component
                                 + ": it does not require the permission " + mConfig.bindPermission);
                         continue;
diff --git a/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java b/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
index ce11268..3bfd93f 100644
--- a/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
@@ -35,7 +35,6 @@
             if (DBG) Slog.d(TAG, "skipping empty notification");
             return null;
         }
-
         record.applyAdjustments();
 
         return null;
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 6a1401c..36bc096 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -16,6 +16,8 @@
 
 package com.android.server.notification;
 
+import android.service.notification.NotificationStats;
+
 import com.android.internal.statusbar.NotificationVisibility;
 
 public interface NotificationDelegate {
@@ -24,7 +26,8 @@
     void onNotificationClick(int callingUid, int callingPid, String key);
     void onNotificationActionClick(int callingUid, int callingPid, String key, int actionIndex);
     void onNotificationClear(int callingUid, int callingPid,
-            String pkg, String tag, int id, int userId);
+            String pkg, String tag, int id, int userId, String key,
+            @NotificationStats.DismissalSurface int dismissalSurface);
     void onNotificationError(int callingUid, int callingPid,
             String pkg, String tag, int id,
             int uid, int initialPid, String message, int userId);
@@ -35,4 +38,6 @@
             NotificationVisibility[] newlyVisibleKeys,
             NotificationVisibility[] noLongerVisibleKeys);
     void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
+    void onNotificationDirectReplied(String key);
+    void onNotificationSettingsViewed(String key);
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4cb5d0f..14cd055 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -134,6 +134,7 @@
 import android.service.notification.NotificationRecordProto;
 import android.service.notification.NotificationServiceDumpProto;
 import android.service.notification.NotificationServiceProto;
+import android.service.notification.NotificationStats;
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenModeConfig;
@@ -678,7 +679,14 @@
 
         @Override
         public void onNotificationClear(int callingUid, int callingPid,
-                String pkg, String tag, int id, int userId) {
+                String pkg, String tag, int id, int userId, String key,
+                @NotificationStats.DismissalSurface int dismissalSurface) {
+            synchronized (mNotificationLock) {
+                NotificationRecord r = mNotificationsByKey.get(key);
+                if (r != null) {
+                    r.recordDismissalSurface(dismissalSurface);
+                }
+            }
             cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
                     Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
                     true, userId, REASON_CANCEL, null);
@@ -764,12 +772,35 @@
                                 .setType(expanded ? MetricsEvent.TYPE_DETAIL
                                         : MetricsEvent.TYPE_COLLAPSE));
                     }
+                    if (expanded) {
+                        r.recordExpanded();
+                    }
                     EventLogTags.writeNotificationExpansion(key,
                             userAction ? 1 : 0, expanded ? 1 : 0,
                             r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
                 }
             }
         }
+
+        @Override
+        public void onNotificationDirectReplied(String key) {
+            synchronized (mNotificationLock) {
+                NotificationRecord r = mNotificationsByKey.get(key);
+                if (r != null) {
+                    r.recordDirectReplied();
+                }
+            }
+        }
+
+        @Override
+        public void onNotificationSettingsViewed(String key) {
+            synchronized (mNotificationLock) {
+                NotificationRecord r = mNotificationsByKey.get(key);
+                if (r != null) {
+                    r.recordViewedSettings();
+                }
+            }
+        }
     };
 
     @GuardedBy("mNotificationLock")
@@ -3830,6 +3861,7 @@
             } else {
                 mSnoozeHelper.snooze(r, mDuration);
             }
+            r.recordSnoozed();
             savePolicyFile();
         }
     }
@@ -3969,7 +4001,7 @@
                         Slog.e(TAG, "Not posting notification without small icon: " + notification);
                         if (old != null && !old.isCanceled) {
                             mListeners.notifyRemovedLocked(n,
-                                    NotificationListenerService.REASON_ERROR);
+                                    NotificationListenerService.REASON_ERROR, null);
                             mHandler.post(new Runnable() {
                                 @Override
                                 public void run() {
@@ -4462,6 +4494,7 @@
             ArrayList<String> groupKeyBefore = new ArrayList<>(N);
             ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
             ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
+            ArrayList<Integer> userSentimentBefore = new ArrayList<>(N);
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
                 orderBefore.add(r.getKey());
@@ -4471,6 +4504,7 @@
                 groupKeyBefore.add(r.getGroupKey());
                 overridePeopleBefore.add(r.getPeopleOverride());
                 snoozeCriteriaBefore.add(r.getSnoozeCriteria());
+                userSentimentBefore.add(r.getUserSentiment());
                 mRankingHelper.extractSignals(r);
             }
             mRankingHelper.sort(mNotificationList);
@@ -4482,7 +4516,8 @@
                         || !Objects.equals(channelBefore.get(i), r.getChannel())
                         || !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
                         || !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
-                        || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())) {
+                        || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())
+                        || !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment())) {
                     mHandler.scheduleSendRankingUpdate();
                     return;
                 }
@@ -4675,6 +4710,10 @@
         // Record caller.
         recordCallerLocked(r);
 
+        if (r.getStats().getDismissalSurface() == NotificationStats.DISMISSAL_NOT_DISMISSED) {
+            r.recordDismissalSurface(NotificationStats.DISMISSAL_OTHER);
+        }
+
         // tell the app
         if (sendDelete) {
             if (r.getNotification().deleteIntent != null) {
@@ -4695,7 +4734,7 @@
                 if (reason != REASON_SNOOZED) {
                     r.isCanceled = true;
                 }
-                mListeners.notifyRemovedLocked(r.sbn, reason);
+                mListeners.notifyRemovedLocked(r.sbn, reason, r.getStats());
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
@@ -5289,6 +5328,7 @@
         Bundle overridePeople = new Bundle();
         Bundle snoozeCriteria = new Bundle();
         Bundle showBadge = new Bundle();
+        Bundle userSentiment = new Bundle();
         for (int i = 0; i < N; i++) {
             NotificationRecord record = mNotificationList.get(i);
             if (!isVisibleToListener(record.sbn, info)) {
@@ -5314,6 +5354,7 @@
             overridePeople.putStringArrayList(key, record.getPeopleOverride());
             snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
             showBadge.putBoolean(key, record.canShowBadge());
+            userSentiment.putInt(key, record.getUserSentiment());
         }
         final int M = keys.size();
         String[] keysAr = keys.toArray(new String[M]);
@@ -5324,7 +5365,7 @@
         }
         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
                 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
-                channels, overridePeople, snoozeCriteria, showBadge);
+                channels, overridePeople, snoozeCriteria, showBadge, userSentiment);
     }
 
     boolean hasCompanionDevice(ManagedServiceInfo info) {
@@ -5413,7 +5454,7 @@
         @Override
         protected Config getConfig() {
             Config c = new Config();
-            c.caption = "notification assistant service";
+            c.caption = "notification assistant";
             c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
             c.xmlTag = TAG_ENABLED_NOTIFICATION_ASSISTANTS;
             c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
@@ -5456,8 +5497,6 @@
                     continue;
                 }
 
-                final int importance = r.getImportance();
-                final boolean fromUser = r.isImportanceFromUser();
                 final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
                 mHandler.post(new Runnable() {
                     @Override
@@ -5488,6 +5527,10 @@
                 final String snoozeCriterionId) {
             TrimCache trimCache = new TrimCache(sbn);
             for (final ManagedServiceInfo info : getServices()) {
+                boolean sbnVisible = isVisibleToListener(sbn, info);
+                if (!sbnVisible) {
+                    continue;
+                }
                 final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
                 mHandler.post(new Runnable() {
                     @Override
@@ -5609,7 +5652,8 @@
                     mHandler.post(new Runnable() {
                         @Override
                         public void run() {
-                            notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
+                            notifyRemoved(
+                                    info, oldSbnLightClone, update, null, REASON_USER_STOPPED);
                         }
                     });
                     continue;
@@ -5629,7 +5673,8 @@
          * asynchronously notify all listeners about a removed notification
          */
         @GuardedBy("mNotificationLock")
-        public void notifyRemovedLocked(StatusBarNotification sbn, int reason) {
+        public void notifyRemovedLocked(StatusBarNotification sbn, int reason,
+                NotificationStats notificationStats) {
             // make a copy in case changes are made to the underlying Notification object
             // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
             // notification
@@ -5638,11 +5683,14 @@
                 if (!isVisibleToListener(sbn, info)) {
                     continue;
                 }
+                // Only assistants can get stats
+                final NotificationStats stats = mAssistants.isServiceTokenValidLocked(info.service)
+                        ? notificationStats : null;
                 final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        notifyRemoved(info, sbnLight, update, reason);
+                        notifyRemoved(info, sbnLight, update, stats, reason);
                     }
                 });
             }
@@ -5747,14 +5795,14 @@
         }
 
         private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
-                NotificationRankingUpdate rankingUpdate, int reason) {
+                NotificationRankingUpdate rankingUpdate, NotificationStats stats, int reason) {
             if (!info.enabledAndUserMatches(sbn.getUserId())) {
                 return;
             }
             final INotificationListener listener = (INotificationListener) info.service;
             StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
             try {
-                listener.onNotificationRemoved(sbnHolder, rankingUpdate, reason);
+                listener.onNotificationRemoved(sbnHolder, rankingUpdate, stats, reason);
             } catch (RemoteException ex) {
                 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
             }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 77bf9e3..faa300f2 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -20,6 +20,8 @@
 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
 import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.service.notification.NotificationListenerService.Ranking
+        .USER_SENTIMENT_NEUTRAL;
 
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -41,6 +43,7 @@
 import android.service.notification.Adjustment;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationRecordProto;
+import android.service.notification.NotificationStats;
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
@@ -84,8 +87,6 @@
 
     NotificationUsageStats.SingleNotificationStats stats;
     boolean isCanceled;
-    /** Whether the notification was seen by the user via one of the notification listeners. */
-    boolean mIsSeen;
 
     // These members are used by NotificationSignalExtractors
     // to communicate with the ranking module.
@@ -136,6 +137,8 @@
     private String mChannelIdLogTag;
 
     private final List<Adjustment> mAdjustments;
+    private final NotificationStats mStats;
+    private int mUserSentiment;
 
     @VisibleForTesting
     public NotificationRecord(Context context, StatusBarNotification sbn,
@@ -156,6 +159,7 @@
         mImportance = calculateImportance();
         mLight = calculateLights();
         mAdjustments = new ArrayList<>();
+        mStats = new NotificationStats();
     }
 
     private boolean isPreChannelsNotification() {
@@ -395,7 +399,7 @@
         pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags));
         pw.println(prefix + "pri=" + notification.priority);
         pw.println(prefix + "key=" + sbn.getKey());
-        pw.println(prefix + "seen=" + mIsSeen);
+        pw.println(prefix + "seen=" + mStats.hasSeen());
         pw.println(prefix + "groupKey=" + getGroupKey());
         pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
         pw.println(prefix + "contentIntent=" + notification.contentIntent);
@@ -572,6 +576,10 @@
                             adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
                     setOverrideGroupKey(groupOverrideKey);
                 }
+                if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) {
+                    setUserSentiment(adjustment.getSignals().getInt(
+                            Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
+                }
             }
         }
     }
@@ -740,6 +748,7 @@
                 .setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)
                 .addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, rank));
         if (visible) {
+            setSeen();
             MetricsLogger.histogram(mContext, "note_freshness", getFreshnessMs(now));
         }
         EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0,
@@ -777,12 +786,12 @@
 
     /** Check if any of the listeners have marked this notification as seen by the user. */
     public boolean isSeen() {
-        return mIsSeen;
+        return mStats.hasSeen();
     }
 
     /** Mark the notification as seen by the user. */
     public void setSeen() {
-        mIsSeen = true;
+        mStats.setSeen();
     }
 
     public void setAuthoritativeRank(int authoritativeRank) {
@@ -883,6 +892,38 @@
         mSnoozeCriteria = snoozeCriteria;
     }
 
+    private void setUserSentiment(int userSentiment) {
+        mUserSentiment = userSentiment;
+    }
+
+    public int getUserSentiment() {
+        return mUserSentiment;
+    }
+
+    public NotificationStats getStats() {
+        return mStats;
+    }
+
+    public void recordExpanded() {
+        mStats.setExpanded();
+    }
+
+    public void recordDirectReplied() {
+        mStats.setDirectReplied();
+    }
+
+    public void recordDismissalSurface(@NotificationStats.DismissalSurface int surface) {
+        mStats.setDismissalSurface(surface);
+    }
+
+    public void recordSnoozed() {
+        mStats.setSnoozed();
+    }
+
+    public void recordViewedSettings() {
+        mStats.setViewedSettings();
+    }
+
     public LogMaker getLogMaker(long now) {
         if (mLogMaker == null) {
             // initialize fields that only change on update (so a new record)
diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index 40c6639..5b3d1ec 100644
--- a/services/core/java/com/android/server/oemlock/OemLockService.java
+++ b/services/core/java/com/android/server/oemlock/OemLockService.java
@@ -31,6 +31,7 @@
 import android.os.UserManagerInternal;
 import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.service.oemlock.IOemLockService;
+import android.service.persistentdata.PersistentDataBlockManager;
 import android.util.Slog;
 
 import com.android.server.LocalServices;
@@ -98,6 +99,7 @@
                         !newRestrictions.getBoolean(UserManager.DISALLOW_FACTORY_RESET);
                 if (!unlockAllowedByAdmin) {
                     mOemLock.setOemUnlockAllowedByDevice(false);
+                    setPersistentDataBlockOemUnlockAllowedBit(false);
                 }
             }
         }
@@ -158,6 +160,7 @@
                 }
 
                 mOemLock.setOemUnlockAllowedByDevice(allowedByUser);
+                setPersistentDataBlockOemUnlockAllowedBit(allowedByUser);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -202,6 +205,20 @@
         }
     };
 
+    /**
+     * Always synchronize the OemUnlockAllowed bit to the FRP partition, which
+     * is used to erase FRP information on a unlockable device.
+     */
+    private void setPersistentDataBlockOemUnlockAllowedBit(boolean allowed) {
+        final PersistentDataBlockManager pdbm = (PersistentDataBlockManager)
+                mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
+        // if mOemLock is PersistentDataBlockLock, then the bit should have already been set
+        if (pdbm != null && !(mOemLock instanceof PersistentDataBlockLock)) {
+            Slog.i(TAG, "Update OEM Unlock bit in pst partition to " + allowed);
+            pdbm.setOemUnlockEnabled(allowed);
+        }
+    }
+
     private boolean isOemUnlockAllowedByAdmin() {
         return !UserManager.get(mContext)
                 .hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET, UserHandle.SYSTEM);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7d1a647..261ae90 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -398,7 +398,7 @@
     static final boolean DEBUG_DOMAIN_VERIFICATION = false;
     private static final boolean DEBUG_BACKUP = false;
     private static final boolean DEBUG_INSTALL = false;
-    private static final boolean DEBUG_REMOVE = false;
+    public static final boolean DEBUG_REMOVE = false;
     private static final boolean DEBUG_BROADCASTS = false;
     private static final boolean DEBUG_SHOW_INFO = false;
     private static final boolean DEBUG_PACKAGE_INFO = false;
@@ -406,7 +406,7 @@
     public static final boolean DEBUG_PACKAGE_SCANNING = false;
     private static final boolean DEBUG_VERIFY = false;
     private static final boolean DEBUG_FILTERS = false;
-    private static final boolean DEBUG_PERMISSIONS = false;
+    public static final boolean DEBUG_PERMISSIONS = false;
     private static final boolean DEBUG_SHARED_LIBRARIES = false;
     private static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE;
 
@@ -954,9 +954,6 @@
     final SparseArray<PackageVerificationState> mPendingVerification
             = new SparseArray<PackageVerificationState>();
 
-    /** Set of packages associated with each app op permission. */
-    final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>();
-
     final PackageInstallerService mInstallerService;
 
     private final PackageDexOptimizer mPackageDexOptimizer;
@@ -2881,7 +2878,7 @@
                         + mSdkVersion + "; regranting permissions for internal storage");
                 updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
             }
-            updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
+            updatePermissionsLocked(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
             ver.sdkVersion = mSdkVersion;
 
             // If this is the first boot or an update from pre-M, and it is a normal
@@ -5182,7 +5179,7 @@
                 final PermissionsState permissionsState = settingBase.getPermissionsState();
                 if (permissionsState.hasPermission(permName, userId)) {
                     if (isUidInstantApp) {
-                        if (mPermissionManager.isPermissionInstant(permName)) {
+                        if (mSettings.mPermissions.isPermissionInstant(permName)) {
                             return PackageManager.PERMISSION_GRANTED;
                         }
                     } else {
@@ -5251,8 +5248,8 @@
         }
     }
 
-    boolean addPermission(PermissionInfo info, final boolean async) {
-        return mPermissionManager.addPermission(
+    private boolean addDynamicPermission(PermissionInfo info, final boolean async) {
+        return mPermissionManager.addDynamicPermission(
                 info, async, getCallingUid(), new PermissionCallback() {
                     @Override
                     public void onPermissionChanged() {
@@ -5268,20 +5265,20 @@
     @Override
     public boolean addPermission(PermissionInfo info) {
         synchronized (mPackages) {
-            return addPermission(info, false);
+            return addDynamicPermission(info, false);
         }
     }
 
     @Override
     public boolean addPermissionAsync(PermissionInfo info) {
         synchronized (mPackages) {
-            return addPermission(info, true);
+            return addDynamicPermission(info, true);
         }
     }
 
     @Override
     public void removePermission(String permName) {
-        mPermissionManager.removePermission(permName, getCallingUid(), mPermissionCallback);
+        mPermissionManager.removeDynamicPermission(permName, getCallingUid(), mPermissionCallback);
     }
 
     @Override
@@ -5942,17 +5939,8 @@
     }
 
     @Override
-    public String[] getAppOpPermissionPackages(String permissionName) {
-        if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
-            return null;
-        }
-        synchronized (mPackages) {
-            ArraySet<String> pkgs = mAppOpPermissionPackages.get(permissionName);
-            if (pkgs == null) {
-                return null;
-            }
-            return pkgs.toArray(new String[pkgs.size()]);
-        }
+    public String[] getAppOpPermissionPackages(String permName) {
+        return mPermissionManager.getAppOpPermissionPackages(permName);
     }
 
     @Override
@@ -10346,7 +10334,7 @@
                             Slog.i(TAG, "Adopting permissions from " + origName + " to "
                                     + pkg.packageName);
                             // SIDE EFFECTS; updates permissions system state; move elsewhere
-                            mSettings.transferPermissionsLPw(origName, pkg.packageName);
+                            mSettings.mPermissions.transferPermissions(origName, pkg.packageName);
                         }
                     }
                 }
@@ -11194,54 +11182,13 @@
                 if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Permission Groups: " + r);
             }
 
-            N = pkg.permissions.size();
-            r = null;
-            for (i=0; i<N; i++) {
-                PackageParser.Permission p = pkg.permissions.get(i);
 
-                // Dont allow ephemeral apps to define new permissions.
-                if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
-                    Slog.w(TAG, "Permission " + p.info.name + " from package "
-                            + p.info.packageName
-                            + " ignored: instant apps cannot define new permissions.");
-                    continue;
-                }
-
-                // Assume by default that we did not install this permission into the system.
-                p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;
-
-                // Now that permission groups have a special meaning, we ignore permission
-                // groups for legacy apps to prevent unexpected behavior. In particular,
-                // permissions for one app being granted to someone just because they happen
-                // to be in a group defined by another app (before this had no implications).
-                if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
-                    p.group = mPermissionGroups.get(p.info.group);
-                    // Warn for a permission in an unknown group.
-                    if (DEBUG_PERMISSIONS && p.info.group != null && p.group == null) {
-                        Slog.i(TAG, "Permission " + p.info.name + " from package "
-                                + p.info.packageName + " in an unknown group " + p.info.group);
-                    }
-                }
-
-                // TODO Move to PermissionManager once mPermissionTrees moves there.
-//                        p.tree ? mSettings.mPermissionTrees
-//                                : mSettings.mPermissions;
-//                final BasePermission bp = BasePermission.createOrUpdate(
-//                        permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees, chatty);
-//                permissionMap.put(p.info.name, bp);
-                if (p.tree) {
-                    final ArrayMap<String, BasePermission> permissionMap =
-                            mSettings.mPermissionTrees;
-                    final BasePermission bp = BasePermission.createOrUpdate(
-                            permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees,
-                            chatty);
-                    permissionMap.put(p.info.name, bp);
-                } else {
-                    final BasePermission bp = BasePermission.createOrUpdate(
-                            (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name),
-                            p, pkg, mSettings.mPermissionTrees, chatty);
-                    mPermissionManager.putPermissionTEMP(p.info.name, bp);
-                }
+            // Dont allow ephemeral apps to define new permissions.
+            if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
+                Slog.w(TAG, "Permissions from package " + pkg.packageName
+                        + " ignored: instant apps cannot define new permissions.");
+            } else {
+                mPermissionManager.addAllPermissions(pkg, chatty);
             }
 
             N = pkg.instrumentation.size();
@@ -11967,53 +11914,7 @@
             if (DEBUG_REMOVE) Log.d(TAG, "  Activities: " + r);
         }
 
-        N = pkg.permissions.size();
-        r = null;
-        for (i=0; i<N; i++) {
-            PackageParser.Permission p = pkg.permissions.get(i);
-            BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name);
-            if (bp == null) {
-                bp = mSettings.mPermissionTrees.get(p.info.name);
-            }
-            if (bp != null && bp.isPermission(p)) {
-                bp.setPermission(null);
-                if (DEBUG_REMOVE && chatty) {
-                    if (r == null) {
-                        r = new StringBuilder(256);
-                    } else {
-                        r.append(' ');
-                    }
-                    r.append(p.info.name);
-                }
-            }
-            if ((p.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
-                ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(p.info.name);
-                if (appOpPkgs != null) {
-                    appOpPkgs.remove(pkg.packageName);
-                }
-            }
-        }
-        if (r != null) {
-            if (DEBUG_REMOVE) Log.d(TAG, "  Permissions: " + r);
-        }
-
-        N = pkg.requestedPermissions.size();
-        r = null;
-        for (i=0; i<N; i++) {
-            String perm = pkg.requestedPermissions.get(i);
-            if (mPermissionManager.isPermissionAppOp(perm)) {
-                ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(perm);
-                if (appOpPkgs != null) {
-                    appOpPkgs.remove(pkg.packageName);
-                    if (appOpPkgs.isEmpty()) {
-                        mAppOpPermissionPackages.remove(perm);
-                    }
-                }
-            }
-        }
-        if (r != null) {
-            if (DEBUG_REMOVE) Log.d(TAG, "  Permissions: " + r);
-        }
+        mPermissionManager.removeAllPermissions(pkg, chatty);
 
         N = pkg.instrumentation.size();
         r = null;
@@ -12074,18 +11975,9 @@
         }
     }
 
-    private static boolean hasPermission(PackageParser.Package pkgInfo, String perm) {
-        for (int i=pkgInfo.permissions.size()-1; i>=0; i--) {
-            if (pkgInfo.permissions.get(i).info.name.equals(perm)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    static final int UPDATE_PERMISSIONS_ALL = 1<<0;
-    static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
-    static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
+    public static final int UPDATE_PERMISSIONS_ALL = 1<<0;
+    public static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
+    public static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
 
     private void updatePermissionsLPw(PackageParser.Package pkg, int flags) {
         // Update the parent permissions
@@ -12101,10 +11993,10 @@
     private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo,
             int flags) {
         final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null;
-        updatePermissionsLPw(changingPkg, pkgInfo, volumeUuid, flags);
+        updatePermissionsLocked(changingPkg, pkgInfo, volumeUuid, flags);
     }
 
-    private void updatePermissionsLPw(String changingPkg,
+    private void updatePermissionsLocked(String changingPkg,
             PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) {
         // TODO: Most of the methods exposing BasePermission internals [source package name,
         // etc..] shouldn't be needed. Instead, when we've parsed a permission that doesn't
@@ -12116,55 +12008,11 @@
         // normal permissions. Today, we need two separate loops because these BasePermission
         // objects are stored separately.
         // Make sure there are no dangling permission trees.
-        Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
-        while (it.hasNext()) {
-            final BasePermission bp = it.next();
-            if (bp.getSourcePackageSetting() == null) {
-                // We may not yet have parsed the package, so just see if
-                // we still know about its settings.
-                bp.setSourcePackageSetting(mSettings.mPackages.get(bp.getSourcePackageName()));
-            }
-            if (bp.getSourcePackageSetting() == null) {
-                Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
-                        + " from package " + bp.getSourcePackageName());
-                it.remove();
-            } else if (changingPkg != null && changingPkg.equals(bp.getSourcePackageName())) {
-                if (pkgInfo == null || !hasPermission(pkgInfo, bp.getName())) {
-                    Slog.i(TAG, "Removing old permission tree: " + bp.getName()
-                            + " from package " + bp.getSourcePackageName());
-                    flags |= UPDATE_PERMISSIONS_ALL;
-                    it.remove();
-                }
-            }
-        }
+        flags = mPermissionManager.updatePermissionTrees(changingPkg, pkgInfo, flags);
 
         // Make sure all dynamic permissions have been assigned to a package,
         // and make sure there are no dangling permissions.
-        final Iterator<BasePermission> permissionIter =
-                mPermissionManager.getPermissionIteratorTEMP();
-        while (permissionIter.hasNext()) {
-            final BasePermission bp = permissionIter.next();
-            if (bp.isDynamic()) {
-                bp.updateDynamicPermission(mSettings.mPermissionTrees);
-            }
-            if (bp.getSourcePackageSetting() == null) {
-                // We may not yet have parsed the package, so just see if
-                // we still know about its settings.
-                bp.setSourcePackageSetting(mSettings.mPackages.get(bp.getSourcePackageName()));
-            }
-            if (bp.getSourcePackageSetting() == null) {
-                Slog.w(TAG, "Removing dangling permission: " + bp.getName()
-                        + " from package " + bp.getSourcePackageName());
-                permissionIter.remove();
-            } else if (changingPkg != null && changingPkg.equals(bp.getSourcePackageName())) {
-                if (pkgInfo == null || !hasPermission(pkgInfo, bp.getName())) {
-                    Slog.i(TAG, "Removing old permission: " + bp.getName()
-                            + " from package " + bp.getSourcePackageName());
-                    flags |= UPDATE_PERMISSIONS_ALL;
-                    permissionIter.remove();
-                }
-            }
-        }
+        flags = mPermissionManager.updatePermissions(changingPkg, pkgInfo, flags);
 
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "grantPermissions");
         // Now update the permissions for all packages, in particular
@@ -12286,12 +12134,7 @@
 
             // Keep track of app op permissions.
             if (bp.isAppOp()) {
-                ArraySet<String> pkgs = mAppOpPermissionPackages.get(perm);
-                if (pkgs == null) {
-                    pkgs = new ArraySet<>();
-                    mAppOpPermissionPackages.put(perm, pkgs);
-                }
-                pkgs.add(pkg.packageName);
+                mSettings.addAppOpPackage(perm, pkg.packageName);
             }
 
             if (bp.isNormal()) {
@@ -21238,7 +21081,7 @@
         // permissions, ensure permissions are updated. Beware of dragons if you
         // try optimizing this.
         synchronized (mPackages) {
-            updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL,
+            updatePermissionsLocked(null, null, StorageManager.UUID_PRIVATE_INTERNAL,
                     UPDATE_PERMISSIONS_ALL);
         }
 
@@ -21289,9 +21132,8 @@
         reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
 
         if (mPrivappPermissionsViolations != null) {
-            Slog.wtf(TAG,"Signature|privileged permissions not in "
+            throw new IllegalStateException("Signature|privileged permissions not in "
                     + "privapp-permissions whitelist: " + mPrivappPermissionsViolations);
-            mPrivappPermissionsViolations = null;
         }
     }
 
@@ -21784,22 +21626,6 @@
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
                 mSettings.dumpPermissionsLPr(pw, packageName, permissionNames, dumpState);
-                if (packageName == null && permissionNames == null) {
-                    for (int iperm=0; iperm<mAppOpPermissionPackages.size(); iperm++) {
-                        if (iperm == 0) {
-                            if (dumpState.onTitlePrinted())
-                                pw.println();
-                            pw.println("AppOp Permissions:");
-                        }
-                        pw.print("  AppOp Permission ");
-                        pw.print(mAppOpPermissionPackages.keyAt(iperm));
-                        pw.println(":");
-                        ArraySet<String> pkgs = mAppOpPermissionPackages.valueAt(iperm);
-                        for (int ipkg=0; ipkg<pkgs.size(); ipkg++) {
-                            pw.print("    "); pw.println(pkgs.valueAt(ipkg));
-                        }
-                    }
-                }
             }
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
@@ -22303,7 +22129,7 @@
                         + mSdkVersion + "; regranting permissions for " + volumeUuid);
                 updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
             }
-            updatePermissionsLPw(null, null, volumeUuid, updateFlags);
+            updatePermissionsLocked(null, null, volumeUuid, updateFlags);
 
             // Yay, everything is now upgraded
             ver.forceCurrent();
@@ -23311,15 +23137,15 @@
     void onNewUserCreated(final int userId) {
         synchronized(mPackages) {
             mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId);
-        }
-        // If permission review for legacy apps is required, we represent
-        // dagerous permissions for such apps as always granted runtime
-        // permissions to keep per user flag state whether review is needed.
-        // Hence, if a new user is added we have to propagate dangerous
-        // permission grants for these legacy apps.
-        if (mPermissionReviewRequired) {
-            updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
-                    | UPDATE_PERMISSIONS_REPLACE_ALL);
+            // If permission review for legacy apps is required, we represent
+            // dagerous permissions for such apps as always granted runtime
+            // permissions to keep per user flag state whether review is needed.
+            // Hence, if a new user is added we have to propagate dangerous
+            // permission grants for these legacy apps.
+            if (mPermissionReviewRequired) {
+                updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
+                        | UPDATE_PERMISSIONS_REPLACE_ALL);
+            }
         }
     }
 
@@ -23763,12 +23589,12 @@
         }
 
         @Override
-        public Object enforcePermissionTreeTEMP(String permName, int callingUid) {
+        public PackageParser.PermissionGroup getPermissionGroupTEMP(String groupName) {
             synchronized (mPackages) {
-                return BasePermission.enforcePermissionTreeLP(
-                        mSettings.mPermissionTrees, permName, callingUid);
+                return mPermissionGroups.get(groupName);
             }
         }
+
         @Override
         public boolean isInstantApp(String packageName, int userId) {
             return PackageManagerService.this.isInstantApp(packageName, userId);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 0084411..56595c9 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -378,10 +378,6 @@
     private final ArrayMap<Long, Integer> mKeySetRefs =
             new ArrayMap<Long, Integer>();
 
-    // Mapping from permission tree names to info about them.
-    final ArrayMap<String, BasePermission> mPermissionTrees =
-            new ArrayMap<String, BasePermission>();
-
     // Packages that have been uninstalled and still need their external
     // storage data deleted.
     final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();
@@ -416,7 +412,7 @@
 
     public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
     /** Settings and other information about permissions */
-    private final PermissionSettings mPermissions;
+    final PermissionSettings mPermissions;
 
     Settings(PermissionSettings permissions, Object lock) {
         this(Environment.getDataDirectory(), permissions, lock);
@@ -622,6 +618,10 @@
         return null;
     }
 
+    void addAppOpPackage(String permName, String packageName) {
+        mPermissions.addAppOpPackage(permName, packageName);
+    }
+
     SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
         SharedUserSetting s = mSharedUsers.get(name);
         if (s != null) {
@@ -666,13 +666,6 @@
     }
 
     /**
-     * Transfers ownership of permissions from one package to another.
-     */
-    void transferPermissionsLPw(String origPackageName, String newPackageName) {
-        mPermissions.transferPermissions(origPackageName, newPackageName, mPermissionTrees);
-    }
-
-    /**
      * Creates a new {@code PackageSetting} object.
      * Use this method instead of the constructor to ensure a settings object is created
      * with the correct base.
@@ -2496,9 +2489,7 @@
             }
 
             serializer.startTag(null, "permission-trees");
-            for (BasePermission bp : mPermissionTrees.values()) {
-                writePermissionLPr(serializer, bp);
-            }
+            mPermissions.writePermissionTrees(serializer);
             serializer.endTag(null, "permission-trees");
 
             serializer.startTag(null, "permissions");
@@ -3042,7 +3033,7 @@
                 } else if (tagName.equals("permissions")) {
                     mPermissions.readPermissions(parser);
                 } else if (tagName.equals("permission-trees")) {
-                    PermissionSettings.readPermissions(mPermissionTrees, parser);
+                    mPermissions.readPermissionTrees(parser);
                 } else if (tagName.equals("shared-user")) {
                     readSharedUserLPw(parser);
                 } else if (tagName.equals("preferred-packages")) {
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 09a6e9c..71d3202 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -48,6 +48,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -77,7 +78,7 @@
 
     final String name;
 
-    @PermissionType final int type;
+    final @PermissionType int type;
 
     String sourcePackageName;
 
@@ -252,12 +253,12 @@
         return changed;
     }
 
-    public void updateDynamicPermission(Map<String, BasePermission> permissionTrees) {
+    public void updateDynamicPermission(Collection<BasePermission> permissionTrees) {
         if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
                 + getName() + " pkg=" + getSourcePackageName()
                 + " info=" + pendingPermissionInfo);
         if (sourcePackageSetting == null && pendingPermissionInfo != null) {
-            final BasePermission tree = findPermissionTreeLP(permissionTrees, name);
+            final BasePermission tree = findPermissionTree(permissionTrees, name);
             if (tree != null && tree.perm != null) {
                 sourcePackageSetting = tree.sourcePackageSetting;
                 perm = new PackageParser.Permission(tree.perm.owner,
@@ -269,8 +270,8 @@
         }
     }
 
-    public static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p,
-            @NonNull PackageParser.Package pkg, Map<String, BasePermission> permissionTrees,
+    static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p,
+            @NonNull PackageParser.Package pkg, Collection<BasePermission> permissionTrees,
             boolean chatty) {
         final PackageSettingBase pkgSetting = (PackageSettingBase) pkg.mExtras;
         // Allow system apps to redefine non-system permissions
@@ -300,7 +301,7 @@
         if (bp.perm == null) {
             if (bp.sourcePackageName == null
                     || bp.sourcePackageName.equals(p.info.packageName)) {
-                final BasePermission tree = findPermissionTreeLP(permissionTrees, p.info.name);
+                final BasePermission tree = findPermissionTree(permissionTrees, p.info.name);
                 if (tree == null
                         || tree.sourcePackageName.equals(p.info.packageName)) {
                     bp.sourcePackageSetting = pkgSetting;
@@ -345,12 +346,12 @@
         return bp;
     }
 
-    public static BasePermission enforcePermissionTreeLP(
-            Map<String, BasePermission> permissionTrees, String permName, int callingUid) {
+    static BasePermission enforcePermissionTree(
+            Collection<BasePermission> permissionTrees, String permName, int callingUid) {
         if (permName != null) {
-            BasePermission bp = findPermissionTreeLP(permissionTrees, permName);
+            BasePermission bp = findPermissionTree(permissionTrees, permName);
             if (bp != null) {
-                if (bp.uid == UserHandle.getAppId(callingUid)) {//UserHandle.getAppId(Binder.getCallingUid())) {
+                if (bp.uid == UserHandle.getAppId(callingUid)) {
                     return bp;
                 }
                 throw new SecurityException("Calling uid " + callingUid
@@ -373,9 +374,9 @@
         }
     }
 
-    private static BasePermission findPermissionTreeLP(
-            Map<String, BasePermission> permissionTrees, String permName) {
-        for (BasePermission bp : permissionTrees.values()) {
+    private static BasePermission findPermissionTree(
+            Collection<BasePermission> permissionTrees, String permName) {
+        for (BasePermission bp : permissionTrees) {
             if (permName.startsWith(bp.name) &&
                     permName.length() > bp.name.length() &&
                     permName.charAt(bp.name.length()) == '.') {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
index 3b20b42..8aac52a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -31,6 +31,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Internal interfaces to be used by other components within the system server.
@@ -81,11 +82,26 @@
             @NonNull int[] allUserIds);
 
 
-    public abstract boolean addPermission(@NonNull PermissionInfo info, boolean async,
+    /**
+     * Add all permissions in the given package.
+     * <p>
+     * NOTE: argument {@code groupTEMP} is temporary until mPermissionGroups is moved to
+     * the permission settings.
+     */
+    public abstract void addAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
+    public abstract void removeAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
+    public abstract boolean addDynamicPermission(@NonNull PermissionInfo info, boolean async,
             int callingUid, @Nullable PermissionCallback callback);
-    public abstract void removePermission(@NonNull String permName, int callingUid,
+    public abstract void removeDynamicPermission(@NonNull String permName, int callingUid,
             @Nullable PermissionCallback callback);
 
+    public abstract int updatePermissions(@Nullable String changingPkg,
+            @Nullable PackageParser.Package pkgInfo, int flags);
+    public abstract int updatePermissionTrees(@Nullable String changingPkg,
+            @Nullable PackageParser.Package pkgInfo, int flags);
+
+    public abstract @Nullable String[] getAppOpPermissionPackages(@NonNull String permName);
+
     public abstract int getPermissionFlags(@NonNull String permName,
             @NonNull String packageName, int callingUid, int userId);
     /**
@@ -98,8 +114,6 @@
      */
     public abstract @Nullable List<PermissionInfo> getPermissionInfoByGroup(@NonNull String group,
             @PermissionInfoFlags int flags, int callingUid);
-    public abstract boolean isPermissionAppOp(@NonNull String permName);
-    public abstract boolean isPermissionInstant(@NonNull String permName);
 
     /**
      * Updates the flags associated with a permission by replacing the flags in
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 6c031a6..062aa33 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -69,6 +69,7 @@
 import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Manages all permissions and handles permissions related tasks.
@@ -260,7 +261,7 @@
 //            }
 
             final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
-            for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
+            for (BasePermission bp : mSettings.mPermissions.values()) {
                 final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags);
                 if (pi != null) {
                     out.add(pi);
@@ -305,7 +306,98 @@
         return protectionLevel;
     }
 
-    private boolean addPermission(
+    private void addAllPermissions(PackageParser.Package pkg, boolean chatty) {
+        final int N = pkg.permissions.size();
+        for (int i=0; i<N; i++) {
+            PackageParser.Permission p = pkg.permissions.get(i);
+
+            // Assume by default that we did not install this permission into the system.
+            p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;
+
+            // Now that permission groups have a special meaning, we ignore permission
+            // groups for legacy apps to prevent unexpected behavior. In particular,
+            // permissions for one app being granted to someone just because they happen
+            // to be in a group defined by another app (before this had no implications).
+            if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
+                p.group = mPackageManagerInt.getPermissionGroupTEMP(p.info.group);
+                // Warn for a permission in an unknown group.
+                if (PackageManagerService.DEBUG_PERMISSIONS
+                        && p.info.group != null && p.group == null) {
+                    Slog.i(TAG, "Permission " + p.info.name + " from package "
+                            + p.info.packageName + " in an unknown group " + p.info.group);
+                }
+            }
+
+            synchronized (PermissionManagerService.this.mLock) {
+                if (p.tree) {
+                    final BasePermission bp = BasePermission.createOrUpdate(
+                            mSettings.getPermissionTreeLocked(p.info.name), p, pkg,
+                            mSettings.getAllPermissionTreesLocked(), chatty);
+                    mSettings.putPermissionTreeLocked(p.info.name, bp);
+                } else {
+                    final BasePermission bp = BasePermission.createOrUpdate(
+                            mSettings.getPermissionLocked(p.info.name),
+                            p, pkg, mSettings.getAllPermissionTreesLocked(), chatty);
+                    mSettings.putPermissionLocked(p.info.name, bp);
+                }
+            }
+        }
+    }
+
+    private void removeAllPermissions(PackageParser.Package pkg, boolean chatty) {
+        synchronized (mLock) {
+            int N = pkg.permissions.size();
+            StringBuilder r = null;
+            for (int i=0; i<N; i++) {
+                PackageParser.Permission p = pkg.permissions.get(i);
+                BasePermission bp = (BasePermission) mSettings.mPermissions.get(p.info.name);
+                if (bp == null) {
+                    bp = mSettings.mPermissionTrees.get(p.info.name);
+                }
+                if (bp != null && bp.isPermission(p)) {
+                    bp.setPermission(null);
+                    if (PackageManagerService.DEBUG_REMOVE && chatty) {
+                        if (r == null) {
+                            r = new StringBuilder(256);
+                        } else {
+                            r.append(' ');
+                        }
+                        r.append(p.info.name);
+                    }
+                }
+                if (p.isAppOp()) {
+                    ArraySet<String> appOpPkgs =
+                            mSettings.mAppOpPermissionPackages.get(p.info.name);
+                    if (appOpPkgs != null) {
+                        appOpPkgs.remove(pkg.packageName);
+                    }
+                }
+            }
+            if (r != null) {
+                if (PackageManagerService.DEBUG_REMOVE) Log.d(TAG, "  Permissions: " + r);
+            }
+
+            N = pkg.requestedPermissions.size();
+            r = null;
+            for (int i=0; i<N; i++) {
+                String perm = pkg.requestedPermissions.get(i);
+                if (mSettings.isPermissionAppOp(perm)) {
+                    ArraySet<String> appOpPkgs = mSettings.mAppOpPermissionPackages.get(perm);
+                    if (appOpPkgs != null) {
+                        appOpPkgs.remove(pkg.packageName);
+                        if (appOpPkgs.isEmpty()) {
+                            mSettings.mAppOpPermissionPackages.remove(perm);
+                        }
+                    }
+                }
+            }
+            if (r != null) {
+                if (PackageManagerService.DEBUG_REMOVE) Log.d(TAG, "  Permissions: " + r);
+            }
+        }
+    }
+
+    private boolean addDynamicPermission(
             PermissionInfo info, int callingUid, PermissionCallback callback) {
         if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
             throw new SecurityException("Instant apps can't add permissions");
@@ -313,8 +405,7 @@
         if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
             throw new SecurityException("Label must be specified in permission");
         }
-        final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP(
-                info.name, callingUid);
+        final BasePermission tree = mSettings.enforcePermissionTree(info.name, callingUid);
         final boolean added;
         final boolean changed;
         synchronized (mLock) {
@@ -341,13 +432,12 @@
         return added;
     }
 
-    private void removePermission(
+    private void removeDynamicPermission(
             String permName, int callingUid, PermissionCallback callback) {
         if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
             throw new SecurityException("Instant applications don't have access to this method");
         }
-        final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP(
-                permName, callingUid);
+        final BasePermission tree = mSettings.enforcePermissionTree(permName, callingUid);
         synchronized (mLock) {
             final BasePermission bp = mSettings.getPermissionLocked(permName);
             if (bp == null) {
@@ -713,7 +803,21 @@
         return runtimePermissionChangedUserIds;
     }
 
-    private int getPermissionFlags(String permName, String packageName, int callingUid, int userId) {
+    private String[] getAppOpPermissionPackages(String permName) {
+        if (mPackageManagerInt.getInstantAppPackageName(Binder.getCallingUid()) != null) {
+            return null;
+        }
+        synchronized (mLock) {
+            final ArraySet<String> pkgs = mSettings.mAppOpPermissionPackages.get(permName);
+            if (pkgs == null) {
+                return null;
+            }
+            return pkgs.toArray(new String[pkgs.size()]);
+        }
+    }
+
+    private int getPermissionFlags(
+            String permName, String packageName, int callingUid, int userId) {
         if (!mUserManagerInt.exists(userId)) {
             return 0;
         }
@@ -741,6 +845,96 @@
         return permissionsState.getPermissionFlags(permName, userId);
     }
 
+    private int updatePermissions(String packageName, PackageParser.Package pkgInfo, int flags) {
+        Set<BasePermission> needsUpdate = null;
+        synchronized (mLock) {
+            final Iterator<BasePermission> it = mSettings.mPermissions.values().iterator();
+            while (it.hasNext()) {
+                final BasePermission bp = it.next();
+                if (bp.isDynamic()) {
+                    bp.updateDynamicPermission(mSettings.mPermissionTrees.values());
+                }
+                if (bp.getSourcePackageSetting() != null) {
+                    if (packageName != null && packageName.equals(bp.getSourcePackageName())
+                        && (pkgInfo == null || !hasPermission(pkgInfo, bp.getName()))) {
+                        Slog.i(TAG, "Removing old permission tree: " + bp.getName()
+                                + " from package " + bp.getSourcePackageName());
+                        flags |= PackageManagerService.UPDATE_PERMISSIONS_ALL;
+                        it.remove();
+                    }
+                    continue;
+                }
+                if (needsUpdate == null) {
+                    needsUpdate = new ArraySet<>(mSettings.mPermissions.size());
+                }
+                needsUpdate.add(bp);
+            }
+        }
+        if (needsUpdate != null) {
+            for (final BasePermission bp : needsUpdate) {
+                final PackageParser.Package pkg =
+                        mPackageManagerInt.getPackage(bp.getSourcePackageName());
+                synchronized (mLock) {
+                    if (pkg != null && pkg.mExtras != null) {
+                        final PackageSetting ps = (PackageSetting) pkg.mExtras;
+                        if (bp.getSourcePackageSetting() == null) {
+                            bp.setSourcePackageSetting(ps);
+                        }
+                        continue;
+                    }
+                    Slog.w(TAG, "Removing dangling permission: " + bp.getName()
+                            + " from package " + bp.getSourcePackageName());
+                    mSettings.removePermissionLocked(bp.getName());
+                }
+            }
+        }
+        return flags;
+    }
+
+    private int updatePermissionTrees(String packageName, PackageParser.Package pkgInfo,
+            int flags) {
+        Set<BasePermission> needsUpdate = null;
+        synchronized (mLock) {
+            final Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
+            while (it.hasNext()) {
+                final BasePermission bp = it.next();
+                if (bp.getSourcePackageSetting() != null) {
+                    if (packageName != null && packageName.equals(bp.getSourcePackageName())
+                        && (pkgInfo == null || !hasPermission(pkgInfo, bp.getName()))) {
+                        Slog.i(TAG, "Removing old permission tree: " + bp.getName()
+                                + " from package " + bp.getSourcePackageName());
+                        flags |= PackageManagerService.UPDATE_PERMISSIONS_ALL;
+                        it.remove();
+                    }
+                    continue;
+                }
+                if (needsUpdate == null) {
+                    needsUpdate = new ArraySet<>(mSettings.mPermissionTrees.size());
+                }
+                needsUpdate.add(bp);
+            }
+        }
+        if (needsUpdate != null) {
+            for (final BasePermission bp : needsUpdate) {
+                final PackageParser.Package pkg =
+                        mPackageManagerInt.getPackage(bp.getSourcePackageName());
+                synchronized (mLock) {
+                    if (pkg != null && pkg.mExtras != null) {
+                        final PackageSetting ps = (PackageSetting) pkg.mExtras;
+                        if (bp.getSourcePackageSetting() == null) {
+                            bp.setSourcePackageSetting(ps);
+                        }
+                        continue;
+                    }
+                    Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
+                            + " from package " + bp.getSourcePackageName());
+                    mSettings.removePermissionLocked(bp.getName());
+                }
+            }
+        }
+        return flags;
+    }
+
     private void updatePermissionFlags(String permName, String packageName, int flagMask,
             int flagValues, int callingUid, int userId, PermissionCallback callback) {
         if (!mUserManagerInt.exists(userId)) {
@@ -872,7 +1066,7 @@
 
     private int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
         int size = 0;
-        for (BasePermission perm : mSettings.getAllPermissionsLocked()) {
+        for (BasePermission perm : mSettings.mPermissions.values()) {
             size += tree.calculateFootprint(perm);
         }
         return size;
@@ -889,6 +1083,15 @@
         }
     }
 
+    private static boolean hasPermission(PackageParser.Package pkgInfo, String permName) {
+        for (int i=pkgInfo.permissions.size()-1; i>=0; i--) {
+            if (pkgInfo.permissions.get(i).info.name.equals(permName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Get the first event id for the permission.
      *
@@ -951,14 +1154,22 @@
 
     private class PermissionManagerInternalImpl extends PermissionManagerInternal {
         @Override
-        public boolean addPermission(PermissionInfo info, boolean async, int callingUid,
-                PermissionCallback callback) {
-            return PermissionManagerService.this.addPermission(info, callingUid, callback);
+        public void addAllPermissions(Package pkg, boolean chatty) {
+            PermissionManagerService.this.addAllPermissions(pkg, chatty);
         }
         @Override
-        public void removePermission(String permName, int callingUid,
+        public void removeAllPermissions(Package pkg, boolean chatty) {
+            PermissionManagerService.this.removeAllPermissions(pkg, chatty);
+        }
+        @Override
+        public boolean addDynamicPermission(PermissionInfo info, boolean async, int callingUid,
                 PermissionCallback callback) {
-            PermissionManagerService.this.removePermission(permName, callingUid, callback);
+            return PermissionManagerService.this.addDynamicPermission(info, callingUid, callback);
+        }
+        @Override
+        public void removeDynamicPermission(String permName, int callingUid,
+                PermissionCallback callback) {
+            PermissionManagerService.this.removeDynamicPermission(permName, callingUid, callback);
         }
         @Override
         public void grantRuntimePermission(String permName, String packageName,
@@ -993,12 +1204,26 @@
                     (SharedUserSetting) suSetting, allUserIds);
         }
         @Override
+        public String[] getAppOpPermissionPackages(String permName) {
+            return PermissionManagerService.this.getAppOpPermissionPackages(permName);
+        }
+        @Override
         public int getPermissionFlags(String permName, String packageName, int callingUid,
                 int userId) {
             return PermissionManagerService.this.getPermissionFlags(permName, packageName,
                     callingUid, userId);
         }
         @Override
+        public int updatePermissions(String packageName,
+                PackageParser.Package pkgInfo, int flags) {
+            return PermissionManagerService.this.updatePermissions(packageName, pkgInfo, flags);
+        }
+        @Override
+        public int updatePermissionTrees(String packageName,
+                PackageParser.Package pkgInfo, int flags) {
+            return PermissionManagerService.this.updatePermissionTrees(packageName, pkgInfo, flags);
+        }
+        @Override
         public void updatePermissionFlags(String permName, String packageName, int flagMask,
                 int flagValues, int callingUid, int userId, PermissionCallback callback) {
             PermissionManagerService.this.updatePermissionFlags(
@@ -1038,20 +1263,6 @@
             return PermissionManagerService.this.getPermissionInfoByGroup(group, flags, callingUid);
         }
         @Override
-        public boolean isPermissionInstant(String permName) {
-            synchronized (PermissionManagerService.this.mLock) {
-                final BasePermission bp = mSettings.getPermissionLocked(permName);
-                return (bp != null && bp.isInstant());
-            }
-        }
-        @Override
-        public boolean isPermissionAppOp(String permName) {
-            synchronized (PermissionManagerService.this.mLock) {
-                final BasePermission bp = mSettings.getPermissionLocked(permName);
-                return (bp != null && bp.isAppOp());
-            }
-        }
-        @Override
         public PermissionSettings getPermissionSettings() {
             return mSettings;
         }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
index 7a2e5ecc..7d125c9 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionSettings.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
@@ -24,6 +24,7 @@
 import android.util.Log;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.XmlUtils;
 import com.android.server.pm.DumpState;
 import com.android.server.pm.PackageManagerService;
@@ -35,6 +36,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.Collection;
+import java.util.Set;
 
 /**
  * Permissions and other related data. This class is not meant for
@@ -49,8 +51,25 @@
      * All of the permissions known to the system. The mapping is from permission
      * name to permission object.
      */
-    private final ArrayMap<String, BasePermission> mPermissions =
+    @GuardedBy("mLock")
+    final ArrayMap<String, BasePermission> mPermissions =
             new ArrayMap<String, BasePermission>();
+
+    /**
+     * All permission trees known to the system. The mapping is from permission tree
+     * name to permission object.
+     */
+    @GuardedBy("mLock")
+    final ArrayMap<String, BasePermission> mPermissionTrees =
+            new ArrayMap<String, BasePermission>();
+
+    /**
+     * Set of packages that request a particular app op. The mapping is from permission
+     * name to package names.
+     */
+    @GuardedBy("mLock")
+    final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>();
+
     private final Object mLock;
 
     PermissionSettings(@NonNull Context context, @NonNull Object lock) {
@@ -65,15 +84,23 @@
         }
     }
 
+    public void addAppOpPackage(String permName, String packageName) {
+        ArraySet<String> pkgs = mAppOpPermissionPackages.get(permName);
+        if (pkgs == null) {
+            pkgs = new ArraySet<>();
+            mAppOpPermissionPackages.put(permName, pkgs);
+        }
+        pkgs.add(packageName);
+    }
+
     /**
      * Transfers ownership of permissions from one package to another.
      */
-    public void transferPermissions(String origPackageName, String newPackageName,
-            ArrayMap<String, BasePermission> permissionTrees) {
+    public void transferPermissions(String origPackageName, String newPackageName) {
         synchronized (mLock) {
             for (int i=0; i<2; i++) {
                 ArrayMap<String, BasePermission> permissions =
-                        i == 0 ? permissionTrees : mPermissions;
+                        i == 0 ? mPermissionTrees : mPermissions;
                 for (BasePermission bp : permissions.values()) {
                     bp.transfer(origPackageName, newPackageName);
                 }
@@ -94,9 +121,26 @@
         }
     }
 
+    public void readPermissionTrees(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        synchronized (mLock) {
+            readPermissions(mPermissionTrees, parser);
+        }
+    }
+
     public void writePermissions(XmlSerializer serializer) throws IOException {
-        for (BasePermission bp : mPermissions.values()) {
-            bp.writeLPr(serializer);
+        synchronized (mLock) {
+            for (BasePermission bp : mPermissions.values()) {
+                bp.writeLPr(serializer);
+            }
+        }
+    }
+
+    public void writePermissionTrees(XmlSerializer serializer) throws IOException {
+        synchronized (mLock) {
+            for (BasePermission bp : mPermissionTrees.values()) {
+                bp.writeLPr(serializer);
+            }
         }
     }
 
@@ -128,6 +172,22 @@
                 printedSomething = bp.dumpPermissionsLPr(pw, packageName, permissionNames,
                         externalStorageEnforced, printedSomething, dumpState);
             }
+            if (packageName == null && permissionNames == null) {
+                for (int iperm = 0; iperm<mAppOpPermissionPackages.size(); iperm++) {
+                    if (iperm == 0) {
+                        if (dumpState.onTitlePrinted())
+                            pw.println();
+                        pw.println("AppOp Permissions:");
+                    }
+                    pw.print("  AppOp Permission ");
+                    pw.print(mAppOpPermissionPackages.keyAt(iperm));
+                    pw.println(":");
+                    ArraySet<String> pkgs = mAppOpPermissionPackages.valueAt(iperm);
+                    for (int ipkg=0; ipkg<pkgs.size(); ipkg++) {
+                        pw.print("    "); pw.println(pkgs.valueAt(ipkg));
+                    }
+                }
+            }
         }
     }
 
@@ -135,15 +195,58 @@
         return mPermissions.get(permName);
     }
 
+    @Nullable BasePermission getPermissionTreeLocked(@NonNull String permName) {
+        return mPermissionTrees.get(permName);
+    }
+
     void putPermissionLocked(@NonNull String permName, @NonNull BasePermission permission) {
         mPermissions.put(permName, permission);
     }
 
+    void putPermissionTreeLocked(@NonNull String permName, @NonNull BasePermission permission) {
+        mPermissionTrees.put(permName, permission);
+    }
+
     void removePermissionLocked(@NonNull String permName) {
         mPermissions.remove(permName);
     }
 
-    Collection<BasePermission> getAllPermissionsLocked() {
+    void removePermissionTreeLocked(@NonNull String permName) {
+        mPermissionTrees.remove(permName);
+    }
+
+    @NonNull Collection<BasePermission> getAllPermissionsLocked() {
         return mPermissions.values();
     }
+
+    @NonNull Collection<BasePermission> getAllPermissionTreesLocked() {
+        return mPermissionTrees.values();
+    }
+
+    /**
+     * Returns the permission tree for the given permission.
+     * @throws SecurityException If the calling UID is not allowed to add permissions to the
+     * found permission tree.
+     */
+    @Nullable BasePermission enforcePermissionTree(@NonNull String permName, int callingUid) {
+        synchronized (mLock) {
+            return BasePermission.enforcePermissionTree(
+                    mPermissionTrees.values(), permName, callingUid);
+        }
+    }
+
+    public boolean isPermissionInstant(String permName) {
+        synchronized (mLock) {
+            final BasePermission bp = mPermissions.get(permName);
+            return (bp != null && bp.isInstant());
+        }
+    }
+
+    boolean isPermissionAppOp(String permName) {
+        synchronized (mLock) {
+            final BasePermission bp = mPermissions.get(permName);
+            return (bp != null && bp.isAppOp());
+        }
+    }
+
 }
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 853e1b2..515fa39 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -457,8 +457,7 @@
         // First send the high-level shut down broadcast.
         mActionDone = false;
         Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
-        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
-                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         mContext.sendOrderedBroadcastAsUser(intent,
                 UserHandle.ALL, null, br, mHandler, 0, null, null);
 
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 05fd248..f1fb3e7 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -161,7 +161,7 @@
         }
     }
 
-    // These values must be kept in sync with cmd/statsd/StatsPuller.h.
+    // These values must be kept in sync with cmd/statsd/StatsPullerManager.h.
     // TODO: pull the constant from stats_events.proto instead
     private static final int PULL_CODE_KERNEL_WAKELOCKS = 20;
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 83fd549..bdfbe48 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -31,6 +31,7 @@
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.os.UserHandle;
+import android.service.notification.NotificationStats;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Slog;
@@ -943,13 +944,15 @@
     }
 
     @Override
-    public void onNotificationClear(String pkg, String tag, int id, int userId) {
+    public void onNotificationClear(String pkg, String tag, int id, int userId, String key,
+            @NotificationStats.DismissalSurface int dismissalSurface) {
         enforceStatusBarService();
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
         long identity = Binder.clearCallingIdentity();
         try {
-            mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId);
+            mNotificationDelegate.onNotificationClear(
+                    callingUid, callingPid, pkg, tag, id, userId, key, dismissalSurface);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -983,6 +986,28 @@
     }
 
     @Override
+    public void onNotificationDirectReplied(String key) throws RemoteException {
+        enforceStatusBarService();
+        long identity = Binder.clearCallingIdentity();
+        try {
+            mNotificationDelegate.onNotificationDirectReplied(key);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void onNotificationSettingsViewed(String key) throws RemoteException {
+        enforceStatusBarService();
+        long identity = Binder.clearCallingIdentity();
+        try {
+            mNotificationDelegate.onNotificationSettingsViewed(key);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
     public void onClearAllNotifications(int userId) {
         enforceStatusBarService();
         final int callingUid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index 6824a59..52b49ba 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -47,6 +47,7 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 import libcore.icu.ICU;
+import libcore.util.TimeZoneFinder;
 import libcore.util.ZoneInfoDB;
 
 import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
@@ -479,9 +480,10 @@
                         case 'a': {
                             // Report the active rules version (i.e. the rules in use by the current
                             // process).
-                            pw.println("Active rules version (ICU, libcore): "
+                            pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
                                     + ICU.getTZDataVersion() + ","
-                                    + ZoneInfoDB.getInstance().getVersion());
+                                    + ZoneInfoDB.getInstance().getVersion() + ","
+                                    + TimeZoneFinder.getInstance().getIanaVersion());
                             break;
                         }
                         default: {
@@ -494,8 +496,10 @@
         }
 
         pw.println("RulesManagerService state: " + toString());
-        pw.println("Active rules version (ICU, libcore): " + ICU.getTZDataVersion() + ","
-                + ZoneInfoDB.getInstance().getVersion());
+        pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
+                + ICU.getTZDataVersion() + ","
+                + ZoneInfoDB.getInstance().getVersion() + ","
+                + TimeZoneFinder.getInstance().getIanaVersion());
         pw.println("Distro state: " + rulesState.toString());
         mPackageTracker.dump(pw);
     }
diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java
index 5721415..95d03d4 100644
--- a/services/core/java/com/android/server/vr/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java
@@ -296,6 +296,7 @@
             flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
             flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
             flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+            flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
             mVirtualDisplay = mDisplayManager.createVirtualDisplay(null /* projection */,
                     DISPLAY_NAME, mVirtualDisplayWidth, mVirtualDisplayHeight, mVirtualDisplayDpi,
                     null /* surface */, flags, null /* callback */, null /* handler */,
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index bdd9de0..7b1e12e 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -74,6 +74,13 @@
     public abstract void onScreenStateChanged(boolean isScreenOn);
 
     /**
+     * Set whether the keyguard is currently active/showing.
+     *
+     * @param isShowing is {@code true} if the keyguard is active/showing.
+     */
+    public abstract void onKeyguardStateChanged(boolean isShowing);
+
+    /**
      * Return NO_ERROR if the given package is installed on the device and enabled as a
      * VrListenerService for the given current user, or a negative error code indicating a failure.
      *
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 1511aee3..e7e4efc 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -115,11 +115,13 @@
     /** Null set of sleep sleep flags. */
     private static final int FLAG_NONE = 0;
     /** Flag set when the device is not sleeping. */
-    private static final int FLAG_AWAKE = 1;
+    private static final int FLAG_AWAKE = 1 << 0;
     /** Flag set when the screen has been turned on. */
-    private static final int FLAG_SCREEN_ON = 2;
+    private static final int FLAG_SCREEN_ON = 1 << 1;
+    /** Flag set when the keyguard is not active. */
+    private static final int FLAG_KEYGUARD_UNLOCKED = 1 << 2;
     /** Flag indicating that all system sleep flags have been set.*/
-    private static final int FLAG_ALL = FLAG_AWAKE | FLAG_SCREEN_ON;
+    private static final int FLAG_ALL = FLAG_AWAKE | FLAG_SCREEN_ON | FLAG_KEYGUARD_UNLOCKED;
 
     private static native void initializeNative();
     private static native void setVrModeNative(boolean enabled);
@@ -155,10 +157,11 @@
     private final NotificationAccessManager mNotifAccessManager = new NotificationAccessManager();
     private INotificationManager mNotificationManager;
     /** Tracks the state of the screen and keyguard UI.*/
-    private int mSystemSleepFlags = FLAG_AWAKE;
+    private int mSystemSleepFlags = FLAG_AWAKE | FLAG_KEYGUARD_UNLOCKED;
     /**
      * Set when ACTION_USER_UNLOCKED is fired. We shouldn't try to bind to the
-     * vr service before then.
+     * vr service before then. This gets set only once the first time the user unlocks the device
+     * and stays true thereafter.
      */
     private boolean mUserUnlocked;
     private Vr2dDisplay mVr2dDisplay;
@@ -208,7 +211,6 @@
                 if (mBootsToVr) {
                     setPersistentVrModeEnabled(true);
                 }
-                consumeAndApplyPendingStateLocked();
                 if (mBootsToVr && !mVrModeEnabled) {
                   setVrMode(true, mDefaultVrService, 0, -1, null);
                 }
@@ -230,29 +232,40 @@
     }
 
     private void setSleepState(boolean isAsleep) {
-        synchronized(mLock) {
-
-            if (!isAsleep) {
-                mSystemSleepFlags |= FLAG_AWAKE;
-            } else {
-                mSystemSleepFlags &= ~FLAG_AWAKE;
-            }
-
-            updateVrModeAllowedLocked();
-        }
+        setSystemState(FLAG_AWAKE, !isAsleep);
     }
 
     private void setScreenOn(boolean isScreenOn) {
+        setSystemState(FLAG_SCREEN_ON, isScreenOn);
+    }
+
+    private void setKeyguardShowing(boolean isShowing) {
+        setSystemState(FLAG_KEYGUARD_UNLOCKED, !isShowing);
+    }
+
+    private void setSystemState(int flags, boolean isOn) {
         synchronized(mLock) {
-            if (isScreenOn) {
-                mSystemSleepFlags |= FLAG_SCREEN_ON;
+            int oldState = mSystemSleepFlags;
+            if (isOn) {
+                mSystemSleepFlags |= flags;
             } else {
-                mSystemSleepFlags &= ~FLAG_SCREEN_ON;
+                mSystemSleepFlags &= ~flags;
             }
-            updateVrModeAllowedLocked();
+            if (oldState != mSystemSleepFlags) {
+                if (DBG) Slog.d(TAG, "System state: " + getStateAsString());
+                updateVrModeAllowedLocked();
+            }
         }
     }
 
+    private String getStateAsString() {
+        return new StringBuilder()
+                .append((mSystemSleepFlags & FLAG_AWAKE) != 0 ? "awake, " : "")
+                .append((mSystemSleepFlags & FLAG_SCREEN_ON) != 0 ? "screen_on, " : "")
+                .append((mSystemSleepFlags & FLAG_KEYGUARD_UNLOCKED) != 0 ? "keyguard_off" : "")
+                .toString();
+    }
+
     private void setUserUnlocked() {
         synchronized(mLock) {
             mUserUnlocked = true;
@@ -672,6 +685,11 @@
         }
 
         @Override
+        public void onKeyguardStateChanged(boolean isShowing) {
+            VrManagerService.this.setKeyguardShowing(isShowing);
+        }
+
+        @Override
         public boolean isCurrentVrListener(String packageName, int userId) {
             return VrManagerService.this.isCurrentVrListener(packageName, userId);
         }
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index c76b905..5365e27 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -16,10 +16,10 @@
 
 package com.android.server.wm;
 
-import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -78,11 +78,8 @@
     // requires that the duration of the two animations are the same.
     SurfaceControl thumbnail;
     int thumbnailTransactionSeq;
-    // TODO(b/62029108): combine both members into a private one. Create a member function to set
-    // the thumbnail layer to +1 to the highest layer position and replace all setter instances
-    // with this function. Remove all unnecessary calls to both variables in other classes.
-    int thumbnailLayer;
-    int thumbnailForceAboveLayer;
+    private int mThumbnailLayer;
+
     Animation thumbnailAnimation;
     final Transformation thumbnailTransformation = new Transformation();
     // This flag indicates that the destruction of the thumbnail surface is synchronized with
@@ -256,7 +253,7 @@
 
     private void updateLayers() {
         mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */);
-        thumbnailLayer = mAppToken.getHighestAnimLayer();
+        updateThumbnailLayer();
     }
 
     private void stepThumbnailAnimation(long currentTime) {
@@ -280,26 +277,35 @@
         thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
         if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
                 "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
-                + " layer=" + thumbnailLayer
+                + " layer=" + mThumbnailLayer
                 + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
                 + "," + tmpFloats[Matrix.MSKEW_Y]
                 + "][" + tmpFloats[Matrix.MSKEW_X]
                 + "," + tmpFloats[Matrix.MSCALE_Y] + "]");
         thumbnail.setAlpha(thumbnailTransformation.getAlpha());
-        if (thumbnailForceAboveLayer > 0) {
-            thumbnail.setLayer(thumbnailForceAboveLayer + 1);
-        } else {
-            // The thumbnail is layered below the window immediately above this
-            // token's anim layer.
-            thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
-                    - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
-        }
+        updateThumbnailLayer();
         thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
                 tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
         thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
     }
 
     /**
+     * Updates the thumbnail layer z order to just above the highest animation layer if changed
+     */
+    void updateThumbnailLayer() {
+        if (thumbnail != null) {
+            final int layer = mAppToken.getHighestAnimLayer();
+            if (layer != mThumbnailLayer) {
+                if (DEBUG_LAYERS) Slog.v(TAG,
+                        "Setting thumbnail layer " + mAppToken + ": layer=" + layer);
+                thumbnail.setLayer(layer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
+                        - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
+                mThumbnailLayer = layer;
+            }
+        }
+    }
+
+    /**
      * Sometimes we need to synchronize the first frame of animation with some external event, e.g.
      * Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
      * and keep producing the first frame of the animation.
@@ -473,7 +479,7 @@
         }
         if (thumbnail != null) {
             pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
-                    pw.print(" layer="); pw.println(thumbnailLayer);
+                    pw.print(" layer="); pw.println(mThumbnailLayer);
             pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
             pw.print(prefix); pw.print("thumbnailTransformation=");
                     pw.println(thumbnailTransformation.toShortString());
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 78f8157..a1eeff8 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1170,7 +1170,6 @@
                 wAppAnimator.thumbnail.destroy();
             }
             wAppAnimator.thumbnail = tAppAnimator.thumbnail;
-            wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer;
             wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation;
             tAppAnimator.thumbnail = null;
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8fc9dda..0e68a8f 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -392,10 +392,6 @@
                     != mTmpWindowAnimator.mAnimTransactionSequence) {
                 appAnimator.thumbnailTransactionSeq =
                         mTmpWindowAnimator.mAnimTransactionSequence;
-                appAnimator.thumbnailLayer = 0;
-            }
-            if (appAnimator.thumbnailLayer < winAnimator.mAnimLayer) {
-                appAnimator.thumbnailLayer = winAnimator.mAnimLayer;
             }
         }
     };
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index fdd2ade..7caf2fe 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -266,20 +266,8 @@
         w.mLayer = layer;
         w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment()
                 + w.getSpecialWindowAnimLayerAdjustment();
-        if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0) {
-            if (w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
-                w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
-            }
-            // TODO(b/62029108): the entire contents of the if statement should call the refactored
-            // function to set the thumbnail layer for w.AppToken
-            int highestLayer = w.mAppToken.getHighestAnimLayer();
-            if (highestLayer > 0) {
-                if (w.mAppToken.mAppAnimator.thumbnail != null
-                        && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer != highestLayer) {
-                    w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = highestLayer;
-                    w.mAppToken.mAppAnimator.thumbnail.setLayer(highestLayer + 1);
-                }
-            }
+        if (w.mAppToken != null) {
+            w.mAppToken.mAppAnimator.updateThumbnailLayer();
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 88625d3..af1fa2f 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -342,10 +342,7 @@
         mTmpLayerAndToken.token = null;
         handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken);
         final AppWindowToken topClosingApp = mTmpLayerAndToken.token;
-        final int topClosingLayer = mTmpLayerAndToken.layer;
-
-        final AppWindowToken topOpeningApp = handleOpeningApps(transit,
-                animLp, voiceInteraction, topClosingLayer);
+        final AppWindowToken topOpeningApp = handleOpeningApps(transit, animLp, voiceInteraction);
 
         mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp);
 
@@ -387,8 +384,9 @@
     }
 
     private AppWindowToken handleOpeningApps(int transit, LayoutParams animLp,
-            boolean voiceInteraction, int topClosingLayer) {
+            boolean voiceInteraction) {
         AppWindowToken topOpeningApp = null;
+        int topOpeningLayer = Integer.MIN_VALUE;
         final int appsCount = mService.mOpeningApps.size();
         for (int i = 0; i < appsCount; i++) {
             AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
@@ -422,7 +420,6 @@
             }
             mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
 
-            int topOpeningLayer = 0;
             if (animLp != null) {
                 final int layer = wtoken.getHighestAnimLayer();
                 if (topOpeningApp == null || layer > topOpeningLayer) {
@@ -431,7 +428,7 @@
                 }
             }
             if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) {
-                createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
+                createThumbnailAppAnimator(transit, wtoken);
             }
         }
         return topOpeningApp;
@@ -473,7 +470,7 @@
                 }
             }
             if (mService.mAppTransition.isNextAppTransitionThumbnailDown()) {
-                createThumbnailAppAnimator(transit, wtoken, 0, layerAndToken.layer);
+                createThumbnailAppAnimator(transit, wtoken);
             }
         }
     }
@@ -666,8 +663,7 @@
         }
     }
 
-    private void createThumbnailAppAnimator(int transit, AppWindowToken appToken,
-            int openingLayer, int closingLayer) {
+    private void createThumbnailAppAnimator(int transit, AppWindowToken appToken) {
         AppWindowAnimator openingAppAnimator = (appToken == null) ? null : appToken.mAppAnimator;
         if (openingAppAnimator == null || openingAppAnimator.animation == null) {
             return;
@@ -724,7 +720,6 @@
                 anim = mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(appRect,
                         insets, thumbnailHeader, taskId, displayConfig.uiMode,
                         displayConfig.orientation);
-                openingAppAnimator.thumbnailForceAboveLayer = Math.max(openingLayer, closingLayer);
                 openingAppAnimator.deferThumbnailDestruction =
                         !mService.mAppTransition.isNextThumbnailTransitionScaleUp();
             } else {
@@ -734,8 +729,8 @@
             anim.restrictDuration(MAX_ANIMATION_DURATION);
             anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
 
+            openingAppAnimator.updateThumbnailLayer();
             openingAppAnimator.thumbnail = surfaceControl;
-            openingAppAnimator.thumbnailLayer = openingLayer;
             openingAppAnimator.thumbnailAnimation = anim;
             mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
         } catch (Surface.OutOfResourcesException e) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 92cbd3d..ff45070 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1640,11 +1640,7 @@
         traceEnd();
 
         traceBeginAndSlog("MakePackageManagerServiceReady");
-        try {
-            mPackageManagerService.systemReady();
-        } catch (Throwable e) {
-            reportWtf("making Package Manager Service ready", e);
-        }
+        mPackageManagerService.systemReady();
         traceEnd();
 
         traceBeginAndSlog("MakeDisplayManagerServiceReady");
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
index 725e8f2..d767ba2 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -16,6 +16,13 @@
 
 package com.android.server.notification;
 
+import static android.service.notification.NotificationListenerService.Ranking
+        .USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking
+        .USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking
+        .USER_SENTIMENT_POSITIVE;
+
 import static org.junit.Assert.assertEquals;
 
 import android.app.NotificationChannel;
@@ -60,6 +67,7 @@
             assertEquals(getPeople(key, i), ranking.getAdditionalPeople());
             assertEquals(getSnoozeCriteria(key, i), ranking.getSnoozeCriteria());
             assertEquals(getShowBadge(i), ranking.canShowBadge());
+            assertEquals(getUserSentiment(i), ranking.getUserSentiment());
         }
     }
 
@@ -74,6 +82,7 @@
         Bundle snoozeCriteria = new Bundle();
         Bundle showBadge = new Bundle();
         int[] importance = new int[mKeys.length];
+        Bundle userSentiment = new Bundle();
 
         for (int i = 0; i < mKeys.length; i++) {
             String key = mKeys[i];
@@ -89,11 +98,12 @@
             overridePeople.putStringArrayList(key, getPeople(key, i));
             snoozeCriteria.putParcelableArrayList(key, getSnoozeCriteria(key, i));
             showBadge.putBoolean(key, getShowBadge(i));
+            userSentiment.putInt(key, getUserSentiment(i));
         }
         NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
                 interceptedKeys.toArray(new String[0]), visibilityOverrides,
                 suppressedVisualEffects, importance, explanation, overrideGroupKeys,
-                channels, overridePeople, snoozeCriteria, showBadge);
+                channels, overridePeople, snoozeCriteria, showBadge, userSentiment);
         return update;
     }
 
@@ -129,6 +139,18 @@
         return index % 3 == 0;
     }
 
+    private int getUserSentiment(int index) {
+        switch(index % 3) {
+            case 0:
+                return USER_SENTIMENT_NEGATIVE;
+            case 1:
+                return USER_SENTIMENT_NEUTRAL;
+            case 2:
+                return USER_SENTIMENT_POSITIVE;
+        }
+        return USER_SENTIMENT_NEUTRAL;
+    }
+
     private ArrayList<String> getPeople(String key, int index) {
         ArrayList<String> people = new ArrayList<>();
         for (int i = 0; i < index; i++) {
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 71a024c..9f5f856 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -38,6 +38,8 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -60,10 +62,13 @@
 import android.media.AudioManager;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Process;
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
+import android.service.notification.Adjustment;
 import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -72,6 +77,7 @@
 import android.util.ArrayMap;
 import android.util.AtomicFile;
 
+import com.android.internal.statusbar.NotificationVisibility;
 import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
 import com.android.server.notification.NotificationManagerService.NotificationAssistants;
@@ -81,8 +87,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.*;
 import org.mockito.stubbing.Answer;
 
 import java.io.BufferedInputStream;
@@ -101,7 +106,7 @@
 public class NotificationManagerServiceTest extends NotificationTestCase {
     private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
     private final int mUid = Binder.getCallingUid();
-    private NotificationManagerService mNotificationManagerService;
+    private NotificationManagerService mService;
     private INotificationManager mBinderService;
     private NotificationManagerInternal mInternalService;
     @Mock
@@ -163,11 +168,11 @@
                 Secure.NOTIFICATION_BADGING, 1,
                 UserHandle.getUserHandleForUid(mUid).getIdentifier());
 
-        mNotificationManagerService = new TestableNotificationManagerService(mContext);
+        mService = new TestableNotificationManagerService(mContext);
 
         // Use this testable looper.
         mTestableLooper = TestableLooper.get(this);
-        mHandler = mNotificationManagerService.new WorkerHandler(mTestableLooper.getLooper());
+        mHandler = mService.new WorkerHandler(mTestableLooper.getLooper());
         // MockPackageManager - default returns ApplicationInfo with matching calling UID
         final ApplicationInfo applicationInfo = new ApplicationInfo();
         applicationInfo.uid = mUid;
@@ -204,7 +209,7 @@
         when(mConditionProviders.getConfig()).thenReturn(dndConfig);
 
         try {
-            mNotificationManagerService.init(mTestableLooper.getLooper(),
+            mService.init(mTestableLooper.getLooper(),
                     mPackageManager, mPackageManagerClient, mockLightsManager,
                     mListeners, mAssistants, mConditionProviders,
                     mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
@@ -214,11 +219,11 @@
                 throw e;
             }
         }
-        mNotificationManagerService.setAudioManager(mAudioManager);
+        mService.setAudioManager(mAudioManager);
 
         // Tests call directly into the Binder.
-        mBinderService = mNotificationManagerService.getBinderService();
-        mInternalService = mNotificationManagerService.getInternalService();
+        mBinderService = mService.getBinderService();
+        mInternalService = mService.getInternalService();
 
         mBinderService.createNotificationChannels(
                 PKG, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
@@ -417,7 +422,7 @@
         NotificationChannel channel = new NotificationChannel("id", "name",
                 IMPORTANCE_HIGH);
         NotificationRecord r = generateNotificationRecord(channel);
-        assertTrue(mNotificationManagerService.isBlocked(r, mUsageStats));
+        assertTrue(mService.isBlocked(r, mUsageStats));
         verify(mUsageStats, times(1)).registerSuspendedByAdmin(eq(r));
     }
 
@@ -428,7 +433,7 @@
         NotificationChannel channel = new NotificationChannel("id", "name",
                 NotificationManager.IMPORTANCE_NONE);
         NotificationRecord r = generateNotificationRecord(channel);
-        assertTrue(mNotificationManagerService.isBlocked(r, mUsageStats));
+        assertTrue(mService.isBlocked(r, mUsageStats));
         verify(mUsageStats, times(1)).registerBlocked(eq(r));
 
         mBinderService.createNotificationChannels(
@@ -457,7 +462,7 @@
         waitForIdle();
         assertEquals(1, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
         assertEquals(IMPORTANCE_LOW,
-                mNotificationManagerService.getNotificationRecord(sbn.getKey()).getImportance());
+                mService.getNotificationRecord(sbn.getKey()).getImportance());
         assertEquals(IMPORTANCE_LOW,
                 mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance());
     }
@@ -485,7 +490,7 @@
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
-        assertNull(mNotificationManagerService.getNotificationRecord(sbn.getKey()));
+        assertNull(mService.getNotificationRecord(sbn.getKey()));
         assertEquals(IMPORTANCE_NONE,
                 mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance());
     }
@@ -493,14 +498,14 @@
     @Test
     public void testBlockedNotifications_blockedChannelGroup() throws Exception {
         when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         when(mRankingHelper.isGroupBlocked(anyString(), anyInt(), anyString())).thenReturn(true);
 
         NotificationChannel channel = new NotificationChannel("id", "name",
                 NotificationManager.IMPORTANCE_HIGH);
         channel.setGroup("something");
         NotificationRecord r = generateNotificationRecord(channel);
-        assertTrue(mNotificationManagerService.isBlocked(r, mUsageStats));
+        assertTrue(mService.isBlocked(r, mUsageStats));
         verify(mUsageStats, times(1)).registerBlocked(eq(r));
     }
 
@@ -529,7 +534,7 @@
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
-        assertNull(mNotificationManagerService.getNotificationRecord(sbn.getKey()));
+        assertNull(mService.getNotificationRecord(sbn.getKey()));
     }
 
     @Test
@@ -539,7 +544,7 @@
         waitForIdle();
         StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
         assertEquals(1, notifs.length);
-        assertEquals(1, mNotificationManagerService.getNotificationRecordCount());
+        assertEquals(1, mService.getNotificationRecordCount());
     }
 
     @Test
@@ -551,7 +556,7 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(PKG);
         assertEquals(0, notifs.length);
-        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+        assertEquals(0, mService.getNotificationRecordCount());
     }
 
     @Test
@@ -566,12 +571,16 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(PKG);
         assertEquals(0, notifs.length);
-        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+        assertEquals(0, mService.getNotificationRecordCount());
+        ArgumentCaptor<NotificationStats> captor = ArgumentCaptor.forClass(NotificationStats.class);
+        verify(mListeners, times(1)).notifyRemovedLocked(any(), anyInt(), captor.capture());
+        assertEquals(NotificationStats.DISMISSAL_OTHER, captor.getValue().getDismissalSurface());
     }
 
     @Test
     public void testCancelNotificationsFromListenerImmediatelyAfterEnqueue() throws Exception {
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        NotificationRecord r = generateNotificationRecord(null);
+        final StatusBarNotification sbn = r.sbn;
         mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelNotificationsFromListener(null, null);
@@ -579,7 +588,7 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(sbn.getPackageName());
         assertEquals(0, notifs.length);
-        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+        assertEquals(0, mService.getNotificationRecordCount());
     }
 
     @Test
@@ -592,7 +601,7 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(sbn.getPackageName());
         assertEquals(0, notifs.length);
-        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+        assertEquals(0, mService.getNotificationRecordCount());
     }
 
     @Test
@@ -604,13 +613,16 @@
                 n.sbn.getId(), n.sbn.getNotification(), n.sbn.getUserId());
         waitForIdle();
 
-        mNotificationManagerService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+        mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
                 n.getUserId());
         waitForIdle();
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(n.sbn.getPackageName());
         assertEquals(0, notifs.length);
-        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+        assertEquals(0, mService.getNotificationRecordCount());
+        ArgumentCaptor<NotificationStats> captor = ArgumentCaptor.forClass(NotificationStats.class);
+        verify(mListeners, times(1)).notifyRemovedLocked(any(), anyInt(), captor.capture());
+        assertEquals(NotificationStats.DISMISSAL_OTHER, captor.getValue().getDismissalSurface());
     }
 
     @Test
@@ -628,7 +640,7 @@
 
         mBinderService.cancelAllNotifications(PKG, parent.sbn.getUserId());
         waitForIdle();
-        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+        assertEquals(0, mService.getNotificationRecordCount());
     }
 
     @Test
@@ -641,7 +653,7 @@
         mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
         waitForIdle();
 
-        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+        assertEquals(0, mService.getNotificationRecordCount());
     }
 
     @Test
@@ -669,7 +681,7 @@
                 parentAsChild.sbn.getUserId());
         waitForIdle();
 
-        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+        assertEquals(0, mService.getNotificationRecordCount());
     }
 
     @Test
@@ -683,7 +695,7 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(sbn.getPackageName());
         assertEquals(1, notifs.length);
-        assertEquals(1, mNotificationManagerService.getNotificationRecordCount());
+        assertEquals(1, mService.getNotificationRecordCount());
     }
 
     @Test
@@ -697,7 +709,7 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(sbn.getPackageName());
         assertEquals(1, notifs.length);
-        assertEquals(1, mNotificationManagerService.getNotificationRecordCount());
+        assertEquals(1, mService.getNotificationRecordCount());
     }
 
     @Test
@@ -710,7 +722,7 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(sbn.getPackageName());
         assertEquals(0, notifs.length);
-        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+        assertEquals(0, mService.getNotificationRecordCount());
     }
 
     @Test
@@ -724,7 +736,7 @@
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(sbn.getPackageName());
         assertEquals(1, notifs.length);
-        assertEquals(1, mNotificationManagerService.getNotificationRecordCount());
+        assertEquals(1, mService.getNotificationRecordCount());
     }
 
     @Test
@@ -745,8 +757,8 @@
         final NotificationRecord notif = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
         notif.getNotification().flags |= Notification.FLAG_NO_CLEAR;
-        mNotificationManagerService.addNotification(notif);
-        mNotificationManagerService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, 0, true,
+        mService.addNotification(notif);
+        mService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, 0, true,
                 notif.getUserId(), 0, null);
         waitForIdle();
         StatusBarNotification[] notifs =
@@ -759,9 +771,9 @@
         final NotificationRecord notif = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
         notif.getNotification().flags |= Notification.FLAG_NO_CLEAR;
-        mNotificationManagerService.addNotification(notif);
+        mService.addNotification(notif);
 
-        mNotificationManagerService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+        mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
                 notif.getUserId());
         waitForIdle();
         StatusBarNotification[] notifs =
@@ -780,11 +792,11 @@
         child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
         final NotificationRecord newGroup = generateNotificationRecord(
                 mTestNotificationChannel, 4, "group2", false);
-        mNotificationManagerService.addNotification(parent);
-        mNotificationManagerService.addNotification(child);
-        mNotificationManagerService.addNotification(child2);
-        mNotificationManagerService.addNotification(newGroup);
-        mNotificationManagerService.getBinderService().cancelNotificationsFromListener(null, null);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        mService.getBinderService().cancelNotificationsFromListener(null, null);
         waitForIdle();
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(parent.sbn.getPackageName());
@@ -802,11 +814,11 @@
         child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
         final NotificationRecord newGroup = generateNotificationRecord(
                 mTestNotificationChannel, 4, "group2", false);
-        mNotificationManagerService.addNotification(parent);
-        mNotificationManagerService.addNotification(child);
-        mNotificationManagerService.addNotification(child2);
-        mNotificationManagerService.addNotification(newGroup);
-        mNotificationManagerService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
+        mService.addNotification(newGroup);
+        mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
                 parent.getUserId());
         waitForIdle();
         StatusBarNotification[] notifs =
@@ -841,7 +853,7 @@
         mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId());
         waitForIdle();
         assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
-        assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+        assertEquals(0, mService.getNotificationRecordCount());
     }
 
     @Test
@@ -849,8 +861,8 @@
         // make sure the same notification can be found in both lists and returned
         final NotificationRecord group1 = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group1", true);
-        mNotificationManagerService.addEnqueuedNotification(group1);
-        mNotificationManagerService.addNotification(group1);
+        mService.addEnqueuedNotification(group1);
+        mService.addNotification(group1);
 
         // should not be returned
         final NotificationRecord group2 = generateNotificationRecord(
@@ -874,7 +886,7 @@
         waitForIdle();
 
         List<NotificationRecord> inGroup1 =
-                mNotificationManagerService.findGroupNotificationsLocked(PKG, group1.getGroupKey(),
+                mService.findGroupNotificationsLocked(PKG, group1.getGroupKey(),
                         group1.sbn.getUserId());
         assertEquals(3, inGroup1.size());
         for (NotificationRecord record : inGroup1) {
@@ -885,8 +897,8 @@
 
     @Test
     public void testTvExtenderChannelOverride_onTv() throws Exception {
-        mNotificationManagerService.setIsTelevision(true);
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setIsTelevision(true);
+        mService.setRankingHelper(mRankingHelper);
         when(mRankingHelper.getNotificationChannel(
                 anyString(), anyInt(), eq("foo"), anyBoolean())).thenReturn(
                         new NotificationChannel("foo", "foo", IMPORTANCE_HIGH));
@@ -900,8 +912,8 @@
 
     @Test
     public void testTvExtenderChannelOverride_notOnTv() throws Exception {
-        mNotificationManagerService.setIsTelevision(false);
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setIsTelevision(false);
+        mService.setRankingHelper(mRankingHelper);
         when(mRankingHelper.getNotificationChannel(
                 anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
                 mTestNotificationChannel);
@@ -918,7 +930,7 @@
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
                 eq(mTestNotificationChannel.getId()), anyBoolean()))
                 .thenReturn(mTestNotificationChannel);
@@ -943,7 +955,7 @@
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         NotificationChannelGroup group1 = new NotificationChannelGroup("a", "b");
         NotificationChannelGroup group2 = new NotificationChannelGroup("n", "m");
 
@@ -963,7 +975,7 @@
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         mTestNotificationChannel.setLightColor(Color.CYAN);
         when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
                 eq(mTestNotificationChannel.getId()), anyBoolean()))
@@ -981,7 +993,7 @@
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
                 eq(mTestNotificationChannel.getId()), anyBoolean()))
                 .thenReturn(mTestNotificationChannel);
@@ -998,7 +1010,7 @@
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
         NotificationChannelGroup ncg = new NotificationChannelGroup("a", "b/c");
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         when(mRankingHelper.getNotificationChannelGroup(eq(ncg.getId()), eq(PKG), anyInt()))
                 .thenReturn(ncg);
         reset(mListeners);
@@ -1010,7 +1022,7 @@
 
     @Test
     public void testUpdateNotificationChannelFromPrivilegedListener_success() throws Exception {
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1028,7 +1040,7 @@
 
     @Test
     public void testUpdateNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         List<String> associations = new ArrayList<>();
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
 
@@ -1050,7 +1062,7 @@
 
     @Test
     public void testUpdateNotificationChannelFromPrivilegedListener_badUser() throws Exception {
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1077,7 +1089,7 @@
 
     @Test
     public void testGetNotificationChannelFromPrivilegedListener_success() throws Exception {
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1091,7 +1103,7 @@
 
     @Test
     public void testGetNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         List<String> associations = new ArrayList<>();
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
 
@@ -1109,7 +1121,7 @@
 
     @Test
     public void testGetNotificationChannelFromPrivilegedListener_badUser() throws Exception {
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1131,7 +1143,7 @@
 
     @Test
     public void testGetNotificationChannelGroupsFromPrivilegedListener_success() throws Exception {
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         List<String> associations = new ArrayList<>();
         associations.add("a");
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1144,7 +1156,7 @@
 
     @Test
     public void testGetNotificationChannelGroupsFromPrivilegedListener_noAccess() throws Exception {
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         List<String> associations = new ArrayList<>();
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
 
@@ -1161,7 +1173,7 @@
 
     @Test
     public void testGetNotificationChannelGroupsFromPrivilegedListener_badUser() throws Exception {
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         List<String> associations = new ArrayList<>();
         when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
         mListener = mock(ManagedServices.ManagedServiceInfo.class);
@@ -1183,14 +1195,14 @@
     public void testHasCompanionDevice_failure() throws Exception {
         when(mCompanionMgr.getAssociations(anyString(), anyInt())).thenThrow(
                 new IllegalArgumentException());
-        mNotificationManagerService.hasCompanionDevice(mListener);
+        mService.hasCompanionDevice(mListener);
     }
 
     @Test
     public void testHasCompanionDevice_noService() throws Exception {
-        mNotificationManagerService = new TestableNotificationManagerService(mContext);
+        mService = new TestableNotificationManagerService(mContext);
 
-        assertFalse(mNotificationManagerService.hasCompanionDevice(mListener));
+        assertFalse(mService.hasCompanionDevice(mListener));
     }
 
     @Test
@@ -1199,16 +1211,17 @@
                 mTestNotificationChannel, 1, null, false);
         final NotificationRecord grouped = generateNotificationRecord(
                 mTestNotificationChannel, 2, "group", false);
-        mNotificationManagerService.addNotification(grouped);
-        mNotificationManagerService.addNotification(nonGrouped);
+        mService.addNotification(grouped);
+        mService.addNotification(nonGrouped);
 
         NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
-                mNotificationManagerService.new SnoozeNotificationRunnable(
+                mService.new SnoozeNotificationRunnable(
                         nonGrouped.getKey(), 100, null);
         snoozeNotificationRunnable.run();
 
         // only snooze the one notification
         verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong());
+        assertTrue(nonGrouped.getStats().hasSnoozed());
     }
 
     @Test
@@ -1219,12 +1232,12 @@
                 mTestNotificationChannel, 2, "group", false);
         final NotificationRecord child2 = generateNotificationRecord(
                 mTestNotificationChannel, 3, "group", false);
-        mNotificationManagerService.addNotification(parent);
-        mNotificationManagerService.addNotification(child);
-        mNotificationManagerService.addNotification(child2);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
 
         NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
-                mNotificationManagerService.new SnoozeNotificationRunnable(
+                mService.new SnoozeNotificationRunnable(
                         parent.getKey(), 100, null);
         snoozeNotificationRunnable.run();
 
@@ -1240,12 +1253,12 @@
                 mTestNotificationChannel, 2, "group", false);
         final NotificationRecord child2 = generateNotificationRecord(
                 mTestNotificationChannel, 3, "group", false);
-        mNotificationManagerService.addNotification(parent);
-        mNotificationManagerService.addNotification(child);
-        mNotificationManagerService.addNotification(child2);
+        mService.addNotification(parent);
+        mService.addNotification(child);
+        mService.addNotification(child2);
 
         NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
-                mNotificationManagerService.new SnoozeNotificationRunnable(
+                mService.new SnoozeNotificationRunnable(
                         child2.getKey(), 100, null);
         snoozeNotificationRunnable.run();
 
@@ -1260,11 +1273,11 @@
         assertTrue(parent.sbn.getNotification().isGroupSummary());
         final NotificationRecord child = generateNotificationRecord(
                 mTestNotificationChannel, 2, "group", false);
-        mNotificationManagerService.addNotification(parent);
-        mNotificationManagerService.addNotification(child);
+        mService.addNotification(parent);
+        mService.addNotification(child);
 
         NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
-                mNotificationManagerService.new SnoozeNotificationRunnable(
+                mService.new SnoozeNotificationRunnable(
                         child.getKey(), 100, null);
         snoozeNotificationRunnable.run();
 
@@ -1276,10 +1289,10 @@
     public void testSnoozeRunnable_snoozeGroupChild_noOthersInGroup() throws Exception {
         final NotificationRecord child = generateNotificationRecord(
                 mTestNotificationChannel, 2, "group", false);
-        mNotificationManagerService.addNotification(child);
+        mService.addNotification(child);
 
         NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
-                mNotificationManagerService.new SnoozeNotificationRunnable(
+                mService.new SnoozeNotificationRunnable(
                         child.getKey(), 100, null);
         snoozeNotificationRunnable.run();
 
@@ -1427,9 +1440,9 @@
     @Test
     public void testOnlyAutogroupIfGroupChanged_noPriorNoti_autogroups() throws Exception {
         NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
-        mNotificationManagerService.addEnqueuedNotification(r);
+        mService.addEnqueuedNotification(r);
         NotificationManagerService.PostNotificationRunnable runnable =
-                mNotificationManagerService.new PostNotificationRunnable(r.getKey());
+                mService.new PostNotificationRunnable(r.getKey());
         runnable.run();
         waitForIdle();
 
@@ -1441,12 +1454,12 @@
             throws Exception {
         NotificationRecord r =
                 generateNotificationRecord(mTestNotificationChannel, 0, "group", false);
-        mNotificationManagerService.addNotification(r);
+        mService.addNotification(r);
 
         r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
-        mNotificationManagerService.addEnqueuedNotification(r);
+        mService.addEnqueuedNotification(r);
         NotificationManagerService.PostNotificationRunnable runnable =
-                mNotificationManagerService.new PostNotificationRunnable(r.getKey());
+                mService.new PostNotificationRunnable(r.getKey());
         runnable.run();
         waitForIdle();
 
@@ -1458,11 +1471,11 @@
             throws Exception {
         NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, "group",
                 false);
-        mNotificationManagerService.addNotification(r);
-        mNotificationManagerService.addEnqueuedNotification(r);
+        mService.addNotification(r);
+        mService.addEnqueuedNotification(r);
 
         NotificationManagerService.PostNotificationRunnable runnable =
-                mNotificationManagerService.new PostNotificationRunnable(r.getKey());
+                mService.new PostNotificationRunnable(r.getKey());
         runnable.run();
         waitForIdle();
 
@@ -1486,7 +1499,7 @@
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
-        NotificationRecord posted = mNotificationManagerService.findNotificationLocked(
+        NotificationRecord posted = mService.findNotificationLocked(
                 PKG, null, nr.sbn.getId(), nr.sbn.getUserId());
 
         assertFalse(posted.getNotification().isColorized());
@@ -1497,12 +1510,12 @@
         for (int i = 0; i < 20; i++) {
             NotificationRecord r =
                     generateNotificationRecord(mTestNotificationChannel, i, null, false);
-            mNotificationManagerService.addEnqueuedNotification(r);
+            mService.addEnqueuedNotification(r);
         }
         for (int i = 0; i < 20; i++) {
             NotificationRecord r =
                     generateNotificationRecord(mTestNotificationChannel, i, null, false);
-            mNotificationManagerService.addNotification(r);
+            mService.addNotification(r);
         }
 
         // another package
@@ -1515,31 +1528,31 @@
                 n, new UserHandle(mUid), null, 0);
         NotificationRecord otherPackage =
                 new NotificationRecord(mContext, sbn, mTestNotificationChannel);
-        mNotificationManagerService.addEnqueuedNotification(otherPackage);
-        mNotificationManagerService.addNotification(otherPackage);
+        mService.addEnqueuedNotification(otherPackage);
+        mService.addNotification(otherPackage);
 
         // Same notifications are enqueued as posted, everything counts b/c id and tag don't match
         int userId = new UserHandle(mUid).getIdentifier();
         assertEquals(40,
-                mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, null));
+                mService.getNotificationCountLocked(PKG, userId, 0, null));
         assertEquals(40,
-                mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag2"));
+                mService.getNotificationCountLocked(PKG, userId, 0, "tag2"));
         assertEquals(2,
-                mNotificationManagerService.getNotificationCountLocked("a", userId, 0, "banana"));
+                mService.getNotificationCountLocked("a", userId, 0, "banana"));
 
         // exclude a known notification - it's excluded from only the posted list, not enqueued
         assertEquals(39,
-                mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag"));
+                mService.getNotificationCountLocked(PKG, userId, 0, "tag"));
     }
 
     @Test
     public void testAddAutogroup_requestsSort() throws Exception {
         RankingHandler rh = mock(RankingHandler.class);
-        mNotificationManagerService.setRankingHandler(rh);
+        mService.setRankingHandler(rh);
 
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
-        mNotificationManagerService.addNotification(r);
-        mNotificationManagerService.addAutogroupKeyLocked(r.getKey());
+        mService.addNotification(r);
+        mService.addAutogroupKeyLocked(r.getKey());
 
         verify(rh, times(1)).requestSort();
     }
@@ -1547,12 +1560,12 @@
     @Test
     public void testRemoveAutogroup_requestsSort() throws Exception {
         RankingHandler rh = mock(RankingHandler.class);
-        mNotificationManagerService.setRankingHandler(rh);
+        mService.setRankingHandler(rh);
 
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         r.setOverrideGroupKey("TEST");
-        mNotificationManagerService.addNotification(r);
-        mNotificationManagerService.removeAutogroupKeyLocked(r.getKey());
+        mService.addNotification(r);
+        mService.removeAutogroupKeyLocked(r.getKey());
 
         verify(rh, times(1)).requestSort();
     }
@@ -1560,47 +1573,47 @@
     @Test
     public void testReaddAutogroup_noSort() throws Exception {
         RankingHandler rh = mock(RankingHandler.class);
-        mNotificationManagerService.setRankingHandler(rh);
+        mService.setRankingHandler(rh);
 
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         r.setOverrideGroupKey("TEST");
-        mNotificationManagerService.addNotification(r);
-        mNotificationManagerService.addAutogroupKeyLocked(r.getKey());
+        mService.addNotification(r);
+        mService.addAutogroupKeyLocked(r.getKey());
 
         verify(rh, never()).requestSort();
     }
 
     @Test
     public void testHandleRankingSort_sendsUpdateOnSignalExtractorChange() throws Exception {
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         NotificationManagerService.WorkerHandler handler = mock(
                 NotificationManagerService.WorkerHandler.class);
-        mNotificationManagerService.setHandler(handler);
+        mService.setHandler(handler);
 
         Map<String, Answer> answers = getSignalExtractorSideEffects();
         for (String message : answers.keySet()) {
-            mNotificationManagerService.clearNotifications();
+            mService.clearNotifications();
             final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
-            mNotificationManagerService.addNotification(r);
+            mService.addNotification(r);
 
             doAnswer(answers.get(message)).when(mRankingHelper).extractSignals(r);
 
-            mNotificationManagerService.handleRankingSort();
+            mService.handleRankingSort();
         }
         verify(handler, times(answers.size())).scheduleSendRankingUpdate();
     }
 
     @Test
     public void testHandleRankingSort_noUpdateWhenNoSignalChange() throws Exception {
-        mNotificationManagerService.setRankingHelper(mRankingHelper);
+        mService.setRankingHelper(mRankingHelper);
         NotificationManagerService.WorkerHandler handler = mock(
                 NotificationManagerService.WorkerHandler.class);
-        mNotificationManagerService.setHandler(handler);
+        mService.setHandler(handler);
 
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
-        mNotificationManagerService.addNotification(r);
+        mService.addNotification(r);
 
-        mNotificationManagerService.handleRankingSort();
+        mService.handleRankingSort();
         verify(handler, never()).scheduleSendRankingUpdate();
     }
 
@@ -1619,7 +1632,7 @@
                 + "<service_listing approved=\"test\" user=\"0\" primary=\"true\" />"
                 + "</dnd_apps>"
                 + "</notification-policy>";
-        mNotificationManagerService.readPolicyXml(
+        mService.readPolicyXml(
                 new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())), false);
         verify(mListeners, times(1)).readXml(any());
         verify(mConditionProviders, times(1)).readXml(any());
@@ -1637,7 +1650,7 @@
                 + "<zen></zen>"
                 + "<ranking></ranking>"
                 + "</notification-policy>";
-        mNotificationManagerService.readPolicyXml(
+        mService.readPolicyXml(
                 new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())), false);
         verify(mListeners, never()).readXml(any());
         verify(mConditionProviders, never()).readXml(any());
@@ -1653,8 +1666,8 @@
     @Test
     public void testLocaleChangedCallsUpdateDefaultZenModeRules() throws Exception {
         ZenModeHelper mZenModeHelper = mock(ZenModeHelper.class);
-        mNotificationManagerService.mZenModeHelper = mZenModeHelper;
-        mNotificationManagerService.mLocaleChangeReceiver.onReceive(mContext,
+        mService.mZenModeHelper = mZenModeHelper;
+        mService.mLocaleChangeReceiver.onReceive(mContext,
                 new Intent(Intent.ACTION_LOCALE_CHANGED));
 
         verify(mZenModeHelper, times(1)).updateDefaultZenRules();
@@ -1685,7 +1698,7 @@
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         assertEquals(IMPORTANCE_LOW,
-                mNotificationManagerService.getNotificationRecord(sbn.getKey()).getImportance());
+                mService.getNotificationRecord(sbn.getKey()).getImportance());
 
         nb = new Notification.Builder(mContext)
                 .setContentTitle("foo")
@@ -1700,10 +1713,85 @@
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         assertEquals(IMPORTANCE_LOW,
-                mNotificationManagerService.getNotificationRecord(sbn.getKey()).getImportance());
+                mService.getNotificationRecord(sbn.getKey()).getImportance());
 
         NotificationChannel defaultChannel = mBinderService.getNotificationChannel(
                 preOPkg, NotificationChannel.DEFAULT_CHANNEL_ID);
         assertEquals(IMPORTANCE_UNSPECIFIED, defaultChannel.getImportance());
     }
+
+    @Test
+    public void testStats_updatedOnDirectReply() throws Exception {
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
+        assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied());
+    }
+
+    @Test
+    public void testStats_updatedOnExpansion() throws Exception {
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true);
+        assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+        mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, false);
+        assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+    }
+
+    @Test
+    public void testStats_updatedOnViewSettings() throws Exception {
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        mService.mNotificationDelegate.onNotificationSettingsViewed(r.getKey());
+        assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasViewedSettings());
+    }
+
+    @Test
+    public void testStats_updatedOnVisibilityChanged() throws Exception {
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 1, true);
+        mService.mNotificationDelegate.onNotificationVisibilityChanged(
+                new NotificationVisibility[] {nv}, new NotificationVisibility[]{});
+        assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen());
+        mService.mNotificationDelegate.onNotificationVisibilityChanged(
+                new NotificationVisibility[] {}, new NotificationVisibility[]{nv});
+        assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen());
+    }
+
+    @Test
+    public void testStats_dismissalSurface() throws Exception {
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+
+        mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.sbn.getTag(),
+                r.sbn.getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD);
+        waitForIdle();
+
+        assertEquals(NotificationStats.DISMISSAL_AOD, r.getStats().getDismissalSurface());
+    }
+
+    @Test
+    public void testUserSentimentChangeTriggersUpdate() throws Exception {
+        final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+        mService.addNotification(r);
+        NotificationManagerService.WorkerHandler handler = mock(
+                NotificationManagerService.WorkerHandler.class);
+        mService.setHandler(handler);
+
+        Bundle signals = new Bundle();
+        signals.putInt(Adjustment.KEY_USER_SENTIMENT,
+                NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+        Adjustment adjustment = new Adjustment(
+                r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+        mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+
+        waitForIdle();
+
+        verify(handler, timeout(300).times(1)).scheduleSendRankingUpdate();
+    }
 }
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
index 5a6225a..ef26705a 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
@@ -15,6 +15,11 @@
  */
 package com.android.server.notification;
 
+import static android.service.notification.NotificationListenerService.Ranking
+        .USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking
+        .USER_SENTIMENT_NEUTRAL;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
@@ -38,8 +43,10 @@
 import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.service.notification.Adjustment;
 import android.service.notification.StatusBarNotification;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -481,5 +488,64 @@
                 record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
     }
 
+    @Test
+    public void testNotificationStats() throws Exception {
+        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, groupId /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
 
+        assertFalse(record.getStats().hasSeen());
+        assertFalse(record.isSeen());
+        assertFalse(record.getStats().hasDirectReplied());
+        assertFalse(record.getStats().hasExpanded());
+        assertFalse(record.getStats().hasInteracted());
+        assertFalse(record.getStats().hasViewedSettings());
+        assertFalse(record.getStats().hasSnoozed());
+
+        record.setSeen();
+        assertTrue(record.getStats().hasSeen());
+        assertTrue(record.isSeen());
+        assertFalse(record.getStats().hasDirectReplied());
+        assertFalse(record.getStats().hasExpanded());
+        assertFalse(record.getStats().hasInteracted());
+        assertFalse(record.getStats().hasViewedSettings());
+        assertFalse(record.getStats().hasSnoozed());
+
+        record.recordViewedSettings();
+        assertFalse(record.getStats().hasDirectReplied());
+        assertFalse(record.getStats().hasExpanded());
+        assertTrue(record.getStats().hasViewedSettings());
+        assertFalse(record.getStats().hasSnoozed());
+
+        record.recordSnoozed();
+        assertFalse(record.getStats().hasDirectReplied());
+        assertFalse(record.getStats().hasExpanded());
+        assertTrue(record.getStats().hasSnoozed());
+
+        record.recordExpanded();
+        assertFalse(record.getStats().hasDirectReplied());
+        assertTrue(record.getStats().hasExpanded());
+
+        record.recordDirectReplied();
+        assertTrue(record.getStats().hasDirectReplied());
+    }
+
+    @Test
+    public void testUserSentiment() throws Exception {
+        StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+                true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+                false /* lights */, false /* defaultLights */, groupId /* group */);
+        NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+        assertEquals(USER_SENTIMENT_NEUTRAL, record.getUserSentiment());
+
+        Bundle signals = new Bundle();
+        signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
+        record.addAdjustment(new Adjustment(pkg, record.getKey(), signals, null, sbn.getUserId()));
+
+        record.applyAdjustments();
+
+        assertEquals(USER_SENTIMENT_NEGATIVE, record.getUserSentiment());
+    }
 }
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationStatsTest.java b/services/tests/notification/src/com/android/server/notification/NotificationStatsTest.java
new file mode 100644
index 0000000..fec2811
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationStatsTest.java
@@ -0,0 +1,93 @@
+package com.android.server.notification;
+
+import static android.service.notification.NotificationStats.DISMISSAL_PEEK;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.service.notification.NotificationStats;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationStatsTest extends NotificationTestCase {
+
+    @Test
+    public void testConstructor() {
+        NotificationStats stats = new NotificationStats();
+
+        assertFalse(stats.hasSeen());
+        assertFalse(stats.hasDirectReplied());
+        assertFalse(stats.hasExpanded());
+        assertFalse(stats.hasInteracted());
+        assertFalse(stats.hasViewedSettings());
+        assertFalse(stats.hasSnoozed());
+        assertEquals(NotificationStats.DISMISSAL_NOT_DISMISSED, stats.getDismissalSurface());
+    }
+
+    @Test
+    public void testSeen() {
+        NotificationStats stats = new NotificationStats();
+        stats.setSeen();
+        assertTrue(stats.hasSeen());
+        assertFalse(stats.hasInteracted());
+    }
+
+    @Test
+    public void testDirectReplied() {
+        NotificationStats stats = new NotificationStats();
+        stats.setDirectReplied();
+        assertTrue(stats.hasDirectReplied());
+        assertTrue(stats.hasInteracted());
+    }
+
+    @Test
+    public void testExpanded() {
+        NotificationStats stats = new NotificationStats();
+        stats.setExpanded();
+        assertTrue(stats.hasExpanded());
+        assertTrue(stats.hasInteracted());
+    }
+
+    @Test
+    public void testSnoozed() {
+        NotificationStats stats = new NotificationStats();
+        stats.setSnoozed();
+        assertTrue(stats.hasSnoozed());
+        assertTrue(stats.hasInteracted());
+    }
+
+    @Test
+    public void testViewedSettings() {
+        NotificationStats stats = new NotificationStats();
+        stats.setViewedSettings();
+        assertTrue(stats.hasViewedSettings());
+        assertTrue(stats.hasInteracted());
+    }
+
+    @Test
+    public void testDismissalSurface() {
+        NotificationStats stats = new NotificationStats();
+        stats.setDismissalSurface(DISMISSAL_PEEK);
+        assertEquals(DISMISSAL_PEEK, stats.getDismissalSurface());
+        assertFalse(stats.hasInteracted());
+    }
+
+    @Test
+    public void testWriteToParcel() {
+        NotificationStats stats = new NotificationStats();
+        stats.setViewedSettings();
+        stats.setDismissalSurface(NotificationStats.DISMISSAL_AOD);
+        Parcel parcel = Parcel.obtain();
+        stats.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        NotificationStats stats1 = NotificationStats.CREATOR.createFromParcel(parcel);
+        assertEquals(stats, stats1);
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
new file mode 100644
index 0000000..b2446ba
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -0,0 +1,987 @@
+/**
+ * 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.server.usage;
+
+import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
+import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
+import static com.android.server.usage.UsageStatsService.MSG_REPORT_EVENT;
+
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.admin.DevicePolicyManager;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.appwidget.AppWidgetManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
+import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
+import android.net.NetworkScoreManager;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.IDeviceIdleController;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages the standby state of an app, listening to various events.
+ */
+public class AppStandbyController {
+
+    private static final String TAG = "AppStandbyController";
+    private static final boolean DEBUG = false;
+
+    static final boolean COMPRESS_TIME = false;
+    private static final long ONE_MINUTE = 60 * 1000;
+
+    // To name the lock for stack traces
+    static class Lock {}
+
+    /** Lock to protect the app's standby state. Required for calls into AppIdleHistory */
+    private final Object mAppIdleLock = new Lock();
+
+    /** Keeps the history and state for each app. */
+    @GuardedBy("mAppIdleLock")
+    private AppIdleHistory mAppIdleHistory;
+
+    @GuardedBy("mAppIdleLock")
+    private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
+            mPackageAccessListeners = new ArrayList<>();
+
+    /** Whether we've queried the list of carrier privileged apps. */
+    @GuardedBy("mAppIdleLock")
+    private boolean mHaveCarrierPrivilegedApps;
+
+    /** List of carrier-privileged apps that should be excluded from standby */
+    @GuardedBy("mAppIdleLock")
+    private List<String> mCarrierPrivilegedApps;
+
+    // Messages for the handler
+    static final int MSG_INFORM_LISTENERS = 3;
+    static final int MSG_FORCE_IDLE_STATE = 4;
+    static final int MSG_CHECK_IDLE_STATES = 5;
+    static final int MSG_CHECK_PAROLE_TIMEOUT = 6;
+    static final int MSG_PAROLE_END_TIMEOUT = 7;
+    static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
+    static final int MSG_PAROLE_STATE_CHANGED = 9;
+    static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
+
+    long mAppIdleScreenThresholdMillis;
+    long mCheckIdleIntervalMillis;
+    long mAppIdleWallclockThresholdMillis;
+    long mAppIdleParoleIntervalMillis;
+    long mAppIdleParoleDurationMillis;
+    boolean mAppIdleEnabled;
+    boolean mAppIdleTempParoled;
+    boolean mCharging;
+    private long mLastAppIdleParoledTime;
+    private boolean mSystemServicesReady = false;
+
+    private volatile boolean mPendingOneTimeCheckIdleStates;
+
+    private final Handler mHandler;
+    private final Context mContext;
+
+    private DisplayManager mDisplayManager;
+    private IDeviceIdleController mDeviceIdleController;
+    private AppWidgetManager mAppWidgetManager;
+    private IBatteryStats mBatteryStats;
+    private PowerManager mPowerManager;
+    private PackageManager mPackageManager;
+    private PackageManagerInternal mPackageManagerInternal;
+
+    AppStandbyController(Context context, Looper looper) {
+        mContext = context;
+        mHandler = new AppStandbyHandler(looper);
+        mPackageManager = mContext.getPackageManager();
+        mAppIdleEnabled = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_enableAutoPowerModes);
+        if (mAppIdleEnabled) {
+            IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+            deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
+            deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+            mContext.registerReceiver(new DeviceStateReceiver(), deviceStates);
+        }
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
+        }
+
+        IntentFilter packageFilter = new IntentFilter();
+        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        packageFilter.addDataScheme("package");
+
+        mContext.registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
+                null, mHandler);
+    }
+
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_SYSTEM_SERVICES_READY) {
+            // Observe changes to the threshold
+            SettingsObserver settingsObserver = new SettingsObserver(mHandler);
+            settingsObserver.registerObserver();
+            settingsObserver.updateSettings();
+
+            mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
+            mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+                    ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+            mBatteryStats = IBatteryStats.Stub.asInterface(
+                    ServiceManager.getService(BatteryStats.SERVICE_NAME));
+            mDisplayManager = (DisplayManager) mContext.getSystemService(
+                    Context.DISPLAY_SERVICE);
+            mPowerManager = mContext.getSystemService(PowerManager.class);
+            mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+
+            mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+            synchronized (mAppIdleLock) {
+                mAppIdleHistory.updateDisplay(isDisplayOn(), SystemClock.elapsedRealtime());
+            }
+
+            if (mPendingOneTimeCheckIdleStates) {
+                postOneTimeCheckIdleStates();
+            }
+
+            mSystemServicesReady = true;
+        } else if (phase == PHASE_BOOT_COMPLETED) {
+            setChargingState(mContext.getSystemService(BatteryManager.class).isCharging());
+        }
+    }
+
+    void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
+        // Get sync adapters for the authority
+        String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
+                authority, userId);
+        for (String packageName: packages) {
+            // Only force the sync adapters to active if the provider is not in the same package and
+            // the sync adapter is a system package.
+            try {
+                PackageInfo pi = mPackageManager.getPackageInfoAsUser(
+                        packageName, PackageManager.MATCH_SYSTEM_ONLY, userId);
+                if (pi == null || pi.applicationInfo == null) {
+                    continue;
+                }
+                if (!packageName.equals(providerPkgName)) {
+                    setAppIdleAsync(packageName, false, userId);
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                // Shouldn't happen
+            }
+        }
+    }
+
+    void setChargingState(boolean charging) {
+        synchronized (mAppIdleLock) {
+            if (mCharging != charging) {
+                mCharging = charging;
+                postParoleStateChanged();
+            }
+        }
+    }
+
+    /** Paroled here means temporary pardon from being inactive */
+    void setAppIdleParoled(boolean paroled) {
+        synchronized (mAppIdleLock) {
+            final long now = System.currentTimeMillis();
+            if (mAppIdleTempParoled != paroled) {
+                mAppIdleTempParoled = paroled;
+                if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled);
+                if (paroled) {
+                    postParoleEndTimeout();
+                } else {
+                    mLastAppIdleParoledTime = now;
+                    postNextParoleTimeout(now);
+                }
+                postParoleStateChanged();
+            }
+        }
+    }
+
+    boolean isParoledOrCharging() {
+        synchronized (mAppIdleLock) {
+            return mAppIdleTempParoled || mCharging;
+        }
+    }
+
+    private void postNextParoleTimeout(long now) {
+        if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT");
+        mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT);
+        // Compute when the next parole needs to happen. We check more frequently than necessary
+        // since the message handler delays are based on elapsedRealTime and not wallclock time.
+        // The comparison is done in wallclock time.
+        long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis) - now;
+        if (timeLeft < 0) {
+            timeLeft = 0;
+        }
+        mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft);
+    }
+
+    private void postParoleEndTimeout() {
+        if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_END_TIMEOUT");
+        mHandler.removeMessages(MSG_PAROLE_END_TIMEOUT);
+        mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, mAppIdleParoleDurationMillis);
+    }
+
+    private void postParoleStateChanged() {
+        if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
+        mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
+        mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
+    }
+
+    void postCheckIdleStates(int userId) {
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
+    }
+
+    /**
+     * We send a different message to check idle states once, otherwise we would end up
+     * scheduling a series of repeating checkIdleStates each time we fired off one.
+     */
+    void postOneTimeCheckIdleStates() {
+        if (mDeviceIdleController == null) {
+            // Not booted yet; wait for it!
+            mPendingOneTimeCheckIdleStates = true;
+        } else {
+            mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
+            mPendingOneTimeCheckIdleStates = false;
+        }
+    }
+
+    /**
+     * Check all running users' or specified user's apps to see if they enter an idle state.
+     * @return Returns whether checking should continue periodically.
+     */
+    boolean checkIdleStates(int checkUserId) {
+        if (!mAppIdleEnabled) {
+            return false;
+        }
+
+        final int[] runningUserIds;
+        try {
+            runningUserIds = ActivityManager.getService().getRunningUserIds();
+            if (checkUserId != UserHandle.USER_ALL
+                    && !ArrayUtils.contains(runningUserIds, checkUserId)) {
+                return false;
+            }
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        for (int i = 0; i < runningUserIds.length; i++) {
+            final int userId = runningUserIds[i];
+            if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
+                continue;
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "Checking idle state for user " + userId);
+            }
+            List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
+                    PackageManager.MATCH_DISABLED_COMPONENTS,
+                    userId);
+            final int packageCount = packages.size();
+            for (int p = 0; p < packageCount; p++) {
+                final PackageInfo pi = packages.get(p);
+                final String packageName = pi.packageName;
+                final boolean isIdle = isAppIdleFiltered(packageName,
+                        UserHandle.getAppId(pi.applicationInfo.uid),
+                        userId, elapsedRealtime);
+                mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
+                        userId, isIdle ? 1 : 0, packageName));
+                if (isIdle) {
+                    synchronized (mAppIdleLock) {
+                        mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
+                    }
+                }
+            }
+        }
+        if (DEBUG) {
+            Slog.d(TAG, "checkIdleStates took "
+                    + (SystemClock.elapsedRealtime() - elapsedRealtime));
+        }
+        return true;
+    }
+
+    /** Check if it's been a while since last parole and let idle apps do some work */
+    void checkParoleTimeout() {
+        boolean setParoled = false;
+        synchronized (mAppIdleLock) {
+            final long now = System.currentTimeMillis();
+            if (!mAppIdleTempParoled) {
+                final long timeSinceLastParole = now - mLastAppIdleParoledTime;
+                if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
+                    if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
+                    setParoled = true;
+                } else {
+                    if (DEBUG) Slog.d(TAG, "Not long enough to go to parole");
+                    postNextParoleTimeout(now);
+                }
+            }
+        }
+        if (setParoled) {
+            setAppIdleParoled(true);
+        }
+    }
+
+    private void notifyBatteryStats(String packageName, int userId, boolean idle) {
+        try {
+            final int uid = mPackageManager.getPackageUidAsUser(packageName,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+            if (idle) {
+                mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
+                        packageName, uid);
+            } else {
+                mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
+                        packageName, uid);
+            }
+        } catch (PackageManager.NameNotFoundException | RemoteException e) {
+        }
+    }
+
+    void onDeviceIdleModeChanged() {
+        final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
+        if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
+        boolean paroled = false;
+        synchronized (mAppIdleLock) {
+            final long timeSinceLastParole = System.currentTimeMillis() - mLastAppIdleParoledTime;
+            if (!deviceIdle
+                    && timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
+                if (DEBUG) {
+                    Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
+                }
+                paroled = true;
+            } else if (deviceIdle) {
+                if (DEBUG) Slog.i(TAG, "Device idle, back to prison");
+                paroled = false;
+            } else {
+                return;
+            }
+        }
+        setAppIdleParoled(paroled);
+    }
+
+    void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) {
+        synchronized (mAppIdleLock) {
+            // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
+            // about apps that are on some kind of whitelist anyway.
+            final boolean previouslyIdle = mAppIdleHistory.isIdle(
+                    event.mPackage, userId, elapsedRealtime);
+            // Inform listeners if necessary
+            if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
+                    || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
+                    || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
+                    || event.mEventType == UsageEvents.Event.USER_INTERACTION)) {
+                mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
+                if (previouslyIdle) {
+                    mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+                                /* idle = */ 0, event.mPackage));
+                    notifyBatteryStats(event.mPackage, userId, false);
+                }
+            }
+        }
+
+    }
+
+    /**
+     * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
+     * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
+     * the threshold for idle.
+     *
+     * This method is always called from the handler thread, so not much synchronization is
+     * required.
+     */
+    void forceIdleState(String packageName, int userId, boolean idle) {
+        final int appId = getAppId(packageName);
+        if (appId < 0) return;
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+
+        final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
+                userId, elapsedRealtime);
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
+        }
+        final boolean stillIdle = isAppIdleFiltered(packageName, appId,
+                userId, elapsedRealtime);
+        // Inform listeners if necessary
+        if (previouslyIdle != stillIdle) {
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+                    /* idle = */ stillIdle ? 1 : 0, packageName));
+            if (!stillIdle) {
+                notifyBatteryStats(packageName, userId, idle);
+            }
+        }
+    }
+
+    public void onUserRemoved(int userId) {
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory.onUserRemoved(userId);
+        }
+    }
+
+    private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
+        synchronized (mAppIdleLock) {
+            return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
+        }
+    }
+
+    void addListener(UsageStatsManagerInternal.AppIdleStateChangeListener listener) {
+        synchronized (mAppIdleLock) {
+            if (!mPackageAccessListeners.contains(listener)) {
+                mPackageAccessListeners.add(listener);
+            }
+        }
+    }
+
+    void removeListener(UsageStatsManagerInternal.AppIdleStateChangeListener listener) {
+        synchronized (mAppIdleLock) {
+            mPackageAccessListeners.remove(listener);
+        }
+    }
+
+    int getAppId(String packageName) {
+        try {
+            ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
+                    PackageManager.MATCH_ANY_USER
+                            | PackageManager.MATCH_DISABLED_COMPONENTS);
+            return ai.uid;
+        } catch (PackageManager.NameNotFoundException re) {
+            return -1;
+        }
+    }
+
+    boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime,
+            boolean shouldObfuscateInstantApps) {
+        if (isParoledOrCharging()) {
+            return false;
+        }
+        if (shouldObfuscateInstantApps &&
+                mPackageManagerInternal.isPackageEphemeral(userId, packageName)) {
+            return false;
+        }
+        return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
+    }
+
+    /**
+     * Checks if an app has been idle for a while and filters out apps that are excluded.
+     * It returns false if the current system state allows all apps to be considered active.
+     * This happens if the device is plugged in or temporarily allowed to make exceptions.
+     * Called by interface impls.
+     */
+    boolean isAppIdleFiltered(String packageName, int appId, int userId,
+            long elapsedRealtime) {
+        if (packageName == null) return false;
+        // If not enabled at all, of course nobody is ever idle.
+        if (!mAppIdleEnabled) {
+            return false;
+        }
+        if (appId < Process.FIRST_APPLICATION_UID) {
+            // System uids never go idle.
+            return false;
+        }
+        if (packageName.equals("android")) {
+            // Nor does the framework (which should be redundant with the above, but for MR1 we will
+            // retain this for safety).
+            return false;
+        }
+        if (mSystemServicesReady) {
+            try {
+                // We allow all whitelisted apps, including those that don't want to be whitelisted
+                // for idle mode, because app idle (aka app standby) is really not as big an issue
+                // for controlling who participates vs. doze mode.
+                if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
+                    return false;
+                }
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+
+            if (isActiveDeviceAdmin(packageName, userId)) {
+                return false;
+            }
+
+            if (isActiveNetworkScorer(packageName)) {
+                return false;
+            }
+
+            if (mAppWidgetManager != null
+                    && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
+                return false;
+            }
+
+            if (isDeviceProvisioningPackage(packageName)) {
+                return false;
+            }
+        }
+
+        if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {
+            return false;
+        }
+
+        // Check this last, as it is the most expensive check
+        // TODO: Optimize this by fetching the carrier privileged apps ahead of time
+        if (isCarrierApp(packageName)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    int[] getIdleUidsForUser(int userId) {
+        if (!mAppIdleEnabled) {
+            return new int[0];
+        }
+
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+
+        List<ApplicationInfo> apps;
+        try {
+            ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager()
+                    .getInstalledApplications(/* flags= */ 0, userId);
+            if (slice == null) {
+                return new int[0];
+            }
+            apps = slice.getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        // State of each uid.  Key is the uid.  Value lower 16 bits is the number of apps
+        // associated with that uid, upper 16 bits is the number of those apps that is idle.
+        SparseIntArray uidStates = new SparseIntArray();
+
+        // Now resolve all app state.  Iterating over all apps, keeping track of how many
+        // we find for each uid and how many of those are idle.
+        for (int i = apps.size() - 1; i >= 0; i--) {
+            ApplicationInfo ai = apps.get(i);
+
+            // Check whether this app is idle.
+            boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
+                    userId, elapsedRealtime);
+
+            int index = uidStates.indexOfKey(ai.uid);
+            if (index < 0) {
+                uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0));
+            } else {
+                int value = uidStates.valueAt(index);
+                uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
+            }
+        }
+        if (DEBUG) {
+            Slog.d(TAG, "getIdleUids took " + (SystemClock.elapsedRealtime() - elapsedRealtime));
+        }
+        int numIdle = 0;
+        for (int i = uidStates.size() - 1; i >= 0; i--) {
+            int value = uidStates.valueAt(i);
+            if ((value&0x7fff) == (value>>16)) {
+                numIdle++;
+            }
+        }
+
+        int[] res = new int[numIdle];
+        numIdle = 0;
+        for (int i = uidStates.size() - 1; i >= 0; i--) {
+            int value = uidStates.valueAt(i);
+            if ((value&0x7fff) == (value>>16)) {
+                res[numIdle] = uidStates.keyAt(i);
+                numIdle++;
+            }
+        }
+
+        return res;
+    }
+
+    void setAppIdleAsync(String packageName, boolean idle, int userId) {
+        if (packageName == null) return;
+
+        mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
+                .sendToTarget();
+    }
+
+    private boolean isActiveDeviceAdmin(String packageName, int userId) {
+        DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+        if (dpm == null) return false;
+        return dpm.packageHasActiveAdmins(packageName, userId);
+    }
+
+    /**
+     * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
+     * returns {@code false}.
+     */
+    private boolean isDeviceProvisioningPackage(String packageName) {
+        String deviceProvisioningPackage = mContext.getResources().getString(
+                com.android.internal.R.string.config_deviceProvisioningPackage);
+        return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
+    }
+
+    private boolean isCarrierApp(String packageName) {
+        synchronized (mAppIdleLock) {
+            if (!mHaveCarrierPrivilegedApps) {
+                fetchCarrierPrivilegedAppsLA();
+            }
+            if (mCarrierPrivilegedApps != null) {
+                return mCarrierPrivilegedApps.contains(packageName);
+            }
+            return false;
+        }
+    }
+
+    void clearCarrierPrivilegedApps() {
+        if (DEBUG) {
+            Slog.i(TAG, "Clearing carrier privileged apps list");
+        }
+        synchronized (mAppIdleLock) {
+            mHaveCarrierPrivilegedApps = false;
+            mCarrierPrivilegedApps = null; // Need to be refetched.
+        }
+    }
+
+    @GuardedBy("mAppIdleLock")
+    private void fetchCarrierPrivilegedAppsLA() {
+        TelephonyManager telephonyManager =
+                mContext.getSystemService(TelephonyManager.class);
+        mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
+        mHaveCarrierPrivilegedApps = true;
+        if (DEBUG) {
+            Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
+        }
+    }
+
+    private boolean isActiveNetworkScorer(String packageName) {
+        NetworkScoreManager nsm = (NetworkScoreManager) mContext.getSystemService(
+                Context.NETWORK_SCORE_SERVICE);
+        return packageName != null && packageName.equals(nsm.getActiveScorerPackage());
+    }
+
+    void informListeners(String packageName, int userId, boolean isIdle) {
+        for (UsageStatsManagerInternal.AppIdleStateChangeListener listener : mPackageAccessListeners) {
+            listener.onAppIdleStateChanged(packageName, userId, isIdle);
+        }
+    }
+
+    void informParoleStateChanged() {
+        final boolean paroled = isParoledOrCharging();
+        for (UsageStatsManagerInternal.AppIdleStateChangeListener listener : mPackageAccessListeners) {
+            listener.onParoleStateChanged(paroled);
+        }
+    }
+
+    void flushToDisk(int userId) {
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory.writeAppIdleTimes(userId);
+        }
+    }
+
+    void flushDurationsToDisk() {
+        // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
+        // considered not-idle, which is the safest outcome in such an event.
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory.writeAppIdleDurations();
+        }
+    }
+
+    boolean isDisplayOn() {
+        return mDisplayManager
+                .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
+    }
+
+    void clearAppIdleForPackage(String packageName, int userId) {
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory.clearUsage(packageName, userId);
+        }
+    }
+
+    private class PackageReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+                    || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+                clearCarrierPrivilegedApps();
+            }
+            if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
+                    Intent.ACTION_PACKAGE_ADDED.equals(action))
+                    && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
+                        getSendingUserId());
+            }
+        }
+    }
+
+    void initializeDefaultsForSystemApps(int userId) {
+        Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
+                PackageManager.MATCH_DISABLED_COMPONENTS,
+                userId);
+        final int packageCount = packages.size();
+        synchronized (mAppIdleLock) {
+            for (int i = 0; i < packageCount; i++) {
+                final PackageInfo pi = packages.get(i);
+                String packageName = pi.packageName;
+                if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
+                    mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime);
+                }
+            }
+        }
+    }
+
+    void postReportContentProviderUsage(String name, String packageName, int userId) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = name;
+        args.arg2 = packageName;
+        args.arg3 = userId;
+        mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args)
+                .sendToTarget();
+    }
+
+    void dumpHistory(IndentingPrintWriter idpw, int userId) {
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory.dumpHistory(idpw, userId);
+        }
+    }
+
+    void dumpUser(IndentingPrintWriter idpw, int userId) {
+        synchronized (mAppIdleLock) {
+            mAppIdleHistory.dump(idpw, userId);
+        }
+    }
+
+    void dumpState(String[] args, PrintWriter pw) {
+        synchronized (mAppIdleLock) {
+            pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
+                    + "): " + mCarrierPrivilegedApps);
+        }
+
+        pw.println();
+        pw.println("Settings:");
+
+        pw.print("  mAppIdleDurationMillis=");
+        TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
+        pw.println();
+
+        pw.print("  mAppIdleWallclockThresholdMillis=");
+        TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw);
+        pw.println();
+
+        pw.print("  mCheckIdleIntervalMillis=");
+        TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
+        pw.println();
+
+        pw.print("  mAppIdleParoleIntervalMillis=");
+        TimeUtils.formatDuration(mAppIdleParoleIntervalMillis, pw);
+        pw.println();
+
+        pw.print("  mAppIdleParoleDurationMillis=");
+        TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw);
+        pw.println();
+
+        pw.println();
+        pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
+        pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
+        pw.print(" mCharging="); pw.print(mCharging);
+        pw.print(" mLastAppIdleParoledTime=");
+        TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
+        pw.println();
+    }
+
+    class AppStandbyHandler extends Handler {
+
+        AppStandbyHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_FORCE_IDLE_STATE:
+                    forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
+                    break;
+
+                case MSG_CHECK_IDLE_STATES:
+                    if (checkIdleStates(msg.arg1)) {
+                        mHandler.sendMessageDelayed(mHandler.obtainMessage(
+                                MSG_CHECK_IDLE_STATES, msg.arg1, 0),
+                                mCheckIdleIntervalMillis);
+                    }
+                    break;
+
+                case MSG_ONE_TIME_CHECK_IDLE_STATES:
+                    mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
+                    checkIdleStates(UserHandle.USER_ALL);
+                    break;
+
+                case MSG_CHECK_PAROLE_TIMEOUT:
+                    checkParoleTimeout();
+                    break;
+
+                case MSG_PAROLE_END_TIMEOUT:
+                    if (DEBUG) Slog.d(TAG, "Ending parole");
+                    setAppIdleParoled(false);
+                    break;
+
+                case MSG_REPORT_CONTENT_PROVIDER_USAGE:
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    reportContentProviderUsage((String) args.arg1, // authority name
+                            (String) args.arg2, // package name
+                            (int) args.arg3); // userId
+                    args.recycle();
+                    break;
+
+                case MSG_PAROLE_STATE_CHANGED:
+                    if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled
+                            + ", Charging state:" + mCharging);
+                    informParoleStateChanged();
+                    break;
+                default:
+                    super.handleMessage(msg);
+                    break;
+
+            }
+        }
+    };
+
+    private class DeviceStateReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
+                setChargingState(intent.getIntExtra("plugged", 0) != 0);
+            } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
+                onDeviceIdleModeChanged();
+            }
+        }
+    }
+
+    private final DisplayManager.DisplayListener mDisplayListener
+            = new DisplayManager.DisplayListener() {
+
+        @Override public void onDisplayAdded(int displayId) {
+        }
+
+        @Override public void onDisplayRemoved(int displayId) {
+        }
+
+        @Override public void onDisplayChanged(int displayId) {
+            if (displayId == Display.DEFAULT_DISPLAY) {
+                final boolean displayOn = isDisplayOn();
+                synchronized (mAppIdleLock) {
+                    mAppIdleHistory.updateDisplay(displayOn, SystemClock.elapsedRealtime());
+                }
+            }
+        }
+    };
+
+    /**
+     * Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}.
+     */
+    private class SettingsObserver extends ContentObserver {
+        /**
+         * This flag has been used to disable app idle on older builds with bug b/26355386.
+         */
+        @Deprecated
+        private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
+
+        private static final String KEY_IDLE_DURATION = "idle_duration2";
+        private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
+        private static final String KEY_PAROLE_INTERVAL = "parole_interval";
+        private static final String KEY_PAROLE_DURATION = "parole_duration";
+
+        private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        void registerObserver() {
+            mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.APP_IDLE_CONSTANTS), false, this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            updateSettings();
+            postOneTimeCheckIdleStates();
+        }
+
+        void updateSettings() {
+            synchronized (mAppIdleLock) {
+                // Look at global settings for this.
+                // TODO: Maybe apply different thresholds for different users.
+                try {
+                    mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
+                            Settings.Global.APP_IDLE_CONSTANTS));
+                } catch (IllegalArgumentException e) {
+                    Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
+                    // fallthrough, mParser is empty and all defaults will be returned.
+                }
+
+                // Default: 12 hours of screen-on time sans dream-time
+                mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
+                        COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
+
+                mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
+                        COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
+
+                mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
+                        COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
+
+                // Default: 24 hours between paroles
+                mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
+                        COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
+
+                mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
+                        COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
+                mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
+                        mAppIdleScreenThresholdMillis);
+            }
+        }
+    }
+
+}
+
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 25e471c..afafea1 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -18,37 +18,24 @@
 
 import android.Manifest;
 import android.app.ActivityManager;
-import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.IUidObserver;
-import android.app.admin.DevicePolicyManager;
 import android.app.usage.ConfigurationStats;
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManagerInternal;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
-import android.appwidget.AppWidgetManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
-import android.database.ContentObserver;
-import android.hardware.display.DisplayManager;
-import android.net.NetworkScoreManager;
-import android.os.BatteryManager;
-import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.FileUtils;
@@ -56,7 +43,6 @@
 import android.os.IDeviceIdleController;
 import android.os.Looper;
 import android.os.Message;
-import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -64,21 +50,12 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.provider.Settings;
-import android.telephony.TelephonyManager;
 import android.util.ArraySet;
-import android.util.KeyValueListParser;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
-import android.util.TimeUtils;
-import android.view.Display;
 
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
@@ -88,7 +65,6 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -107,7 +83,6 @@
     static final boolean COMPRESS_TIME = false;
 
     private static final long TEN_SECONDS = 10 * 1000;
-    private static final long ONE_MINUTE = 60 * 1000;
     private static final long TWENTY_MINUTES = 20 * 60 * 1000;
     private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES;
     private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
@@ -115,24 +90,10 @@
     private static final boolean ENABLE_KERNEL_UPDATES = true;
     private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set");
 
-    long mAppIdleScreenThresholdMillis;
-    long mCheckIdleIntervalMillis;
-    long mAppIdleWallclockThresholdMillis;
-    long mAppIdleParoleIntervalMillis;
-    long mAppIdleParoleDurationMillis;
-
     // Handler message types.
     static final int MSG_REPORT_EVENT = 0;
     static final int MSG_FLUSH_TO_DISK = 1;
     static final int MSG_REMOVE_USER = 2;
-    static final int MSG_INFORM_LISTENERS = 3;
-    static final int MSG_FORCE_IDLE_STATE = 4;
-    static final int MSG_CHECK_IDLE_STATES = 5;
-    static final int MSG_CHECK_PAROLE_TIMEOUT = 6;
-    static final int MSG_PAROLE_END_TIMEOUT = 7;
-    static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
-    static final int MSG_PAROLE_STATE_CHANGED = 9;
-    static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
 
     private final Object mLock = new Object();
     Handler mHandler;
@@ -140,11 +101,7 @@
     UserManager mUserManager;
     PackageManager mPackageManager;
     PackageManagerInternal mPackageManagerInternal;
-    AppWidgetManager mAppWidgetManager;
     IDeviceIdleController mDeviceIdleController;
-    private DisplayManager mDisplayManager;
-    private PowerManager mPowerManager;
-    private IBatteryStats mBatteryStats;
 
     private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
     private final SparseIntArray mUidToKernelCounter = new SparseIntArray();
@@ -152,26 +109,7 @@
     long mRealTimeSnapshot;
     long mSystemTimeSnapshot;
 
-    boolean mAppIdleEnabled;
-    boolean mAppIdleTempParoled;
-    boolean mCharging;
-    private long mLastAppIdleParoledTime;
-
-    private volatile boolean mPendingOneTimeCheckIdleStates;
-    private boolean mSystemServicesReady = false;
-
-    private final Object mAppIdleLock = new Object();
-    @GuardedBy("mAppIdleLock")
-    private AppIdleHistory mAppIdleHistory;
-
-    @GuardedBy("mAppIdleLock")
-    private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
-            mPackageAccessListeners = new ArrayList<>();
-
-    @GuardedBy("mAppIdleLock")
-    private boolean mHaveCarrierPrivilegedApps;
-    @GuardedBy("mAppIdleLock")
-    private List<String> mCarrierPrivilegedApps;
+    AppStandbyController mAppStandby;
 
     public UsageStatsService(Context context) {
         super(context);
@@ -185,6 +123,8 @@
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         mHandler = new H(BackgroundThread.get().getLooper());
 
+        mAppStandby = new AppStandbyController(getContext(), BackgroundThread.get().getLooper());
+
         File systemDataDir = new File(Environment.getDataDirectory(), "system");
         mUsageStatsDir = new File(systemDataDir, "usagestats");
         mUsageStatsDir.mkdirs();
@@ -198,30 +138,9 @@
         getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, filter,
                 null, mHandler);
 
-        IntentFilter packageFilter = new IntentFilter();
-        packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        packageFilter.addDataScheme("package");
-
-        getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
-                null, mHandler);
-
-        mAppIdleEnabled = getContext().getResources().getBoolean(
-                com.android.internal.R.bool.config_enableAutoPowerModes);
-        if (mAppIdleEnabled) {
-            IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
-            deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
-            deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
-            getContext().registerReceiver(new DeviceStateReceiver(), deviceStates);
-        }
-
         synchronized (mLock) {
             cleanUpRemovedUsersLocked();
         }
-        synchronized (mAppIdleLock) {
-            mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
-        }
 
         mRealTimeSnapshot = SystemClock.elapsedRealtime();
         mSystemTimeSnapshot = System.currentTimeMillis();
@@ -233,28 +152,10 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == PHASE_SYSTEM_SERVICES_READY) {
-            // Observe changes to the threshold
-            SettingsObserver settingsObserver = new SettingsObserver(mHandler);
-            settingsObserver.registerObserver();
-            settingsObserver.updateSettings();
+            mAppStandby.onBootPhase(phase);
 
-            mAppWidgetManager = getContext().getSystemService(AppWidgetManager.class);
             mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
                     ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
-            mBatteryStats = IBatteryStats.Stub.asInterface(
-                    ServiceManager.getService(BatteryStats.SERVICE_NAME));
-            mDisplayManager = (DisplayManager) getContext().getSystemService(
-                    Context.DISPLAY_SERVICE);
-            mPowerManager = getContext().getSystemService(PowerManager.class);
-
-            mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
-            synchronized (mAppIdleLock) {
-                mAppIdleHistory.updateDisplay(isDisplayOn(), SystemClock.elapsedRealtime());
-            }
-
-            if (mPendingOneTimeCheckIdleStates) {
-                postOneTimeCheckIdleStates();
-            }
 
             if (ENABLE_KERNEL_UPDATES && KERNEL_COUNTER_FILE.exists()) {
                 try {
@@ -268,18 +169,9 @@
             } else {
                 Slog.w(TAG, "Missing procfs interface: " + KERNEL_COUNTER_FILE);
             }
-
-            mSystemServicesReady = true;
-        } else if (phase == PHASE_BOOT_COMPLETED) {
-            setChargingState(getContext().getSystemService(BatteryManager.class).isCharging());
         }
     }
 
-    private boolean isDisplayOn() {
-        return mDisplayManager
-                .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
-    }
-
     private class UserActionsReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -291,60 +183,12 @@
                 }
             } else if (Intent.ACTION_USER_STARTED.equals(action)) {
                 if (userId >=0) {
-                    postCheckIdleStates(userId);
+                    mAppStandby.postCheckIdleStates(userId);
                 }
             }
         }
     }
 
-    private class PackageReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (Intent.ACTION_PACKAGE_ADDED.equals(action)
-                    || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
-                clearCarrierPrivilegedApps();
-            }
-            if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
-                    Intent.ACTION_PACKAGE_ADDED.equals(action))
-                    && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
-                clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
-                        getSendingUserId());
-            }
-        }
-    }
-
-    private class DeviceStateReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
-                setChargingState(intent.getIntExtra("plugged", 0) != 0);
-            } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
-                onDeviceIdleModeChanged();
-            }
-        }
-    }
-
-    private final DisplayManager.DisplayListener mDisplayListener
-            = new DisplayManager.DisplayListener() {
-
-        @Override public void onDisplayAdded(int displayId) {
-        }
-
-        @Override public void onDisplayRemoved(int displayId) {
-        }
-
-        @Override public void onDisplayChanged(int displayId) {
-            if (displayId == Display.DEFAULT_DISPLAY) {
-                final boolean displayOn = isDisplayOn();
-                synchronized (UsageStatsService.this.mAppIdleLock) {
-                    mAppIdleHistory.updateDisplay(displayOn, SystemClock.elapsedRealtime());
-                }
-            }
-        }
-    };
-
     private final IUidObserver mUidObserver = new IUidObserver.Stub() {
         @Override
         public void onUidStateChanged(int uid, int procState, long procStateSeq) {
@@ -388,42 +232,18 @@
 
     @Override
     public void onStatsReloaded() {
-        postOneTimeCheckIdleStates();
+        mAppStandby.postOneTimeCheckIdleStates();
     }
 
     @Override
     public void onNewUpdate(int userId) {
-        initializeDefaultsForSystemApps(userId);
-    }
-
-    private void initializeDefaultsForSystemApps(int userId) {
-        Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
-        final long elapsedRealtime = SystemClock.elapsedRealtime();
-        List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
-                PackageManager.MATCH_DISABLED_COMPONENTS,
-                userId);
-        final int packageCount = packages.size();
-        synchronized (mAppIdleLock) {
-            for (int i = 0; i < packageCount; i++) {
-                final PackageInfo pi = packages.get(i);
-                String packageName = pi.packageName;
-                if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
-                    mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime);
-                }
-            }
-        }
+        mAppStandby.initializeDefaultsForSystemApps(userId);
     }
 
     private boolean shouldObfuscateInstantAppsForCaller(int callingUid, int userId) {
         return !mPackageManagerInternal.canAccessInstantApps(callingUid, userId);
     }
 
-    void clearAppIdleForPackage(String packageName, int userId) {
-        synchronized (mAppIdleLock) {
-            mAppIdleHistory.clearUsage(packageName, userId);
-        }
-    }
-
     private void cleanUpRemovedUsersLocked() {
         final List<UserInfo> users = mUserManager.getUsers(true);
         if (users == null || users.size() == 0) {
@@ -451,195 +271,6 @@
         }
     }
 
-    void setChargingState(boolean charging) {
-        synchronized (mAppIdleLock) {
-            if (mCharging != charging) {
-                mCharging = charging;
-                postParoleStateChanged();
-            }
-        }
-    }
-
-    /** Paroled here means temporary pardon from being inactive */
-    void setAppIdleParoled(boolean paroled) {
-        synchronized (mAppIdleLock) {
-            final long now = System.currentTimeMillis();
-            if (mAppIdleTempParoled != paroled) {
-                mAppIdleTempParoled = paroled;
-                if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled);
-                if (paroled) {
-                    postParoleEndTimeout();
-                } else {
-                    mLastAppIdleParoledTime = now;
-                    postNextParoleTimeout(now);
-                }
-                postParoleStateChanged();
-            }
-        }
-    }
-
-    boolean isParoledOrCharging() {
-        synchronized (mAppIdleLock) {
-            return mAppIdleTempParoled || mCharging;
-        }
-    }
-
-    private void postNextParoleTimeout(long now) {
-        if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT");
-        mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT);
-        // Compute when the next parole needs to happen. We check more frequently than necessary
-        // since the message handler delays are based on elapsedRealTime and not wallclock time.
-        // The comparison is done in wallclock time.
-        long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis) - now;
-        if (timeLeft < 0) {
-            timeLeft = 0;
-        }
-        mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft);
-    }
-
-    private void postParoleEndTimeout() {
-        if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_END_TIMEOUT");
-        mHandler.removeMessages(MSG_PAROLE_END_TIMEOUT);
-        mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, mAppIdleParoleDurationMillis);
-    }
-
-    private void postParoleStateChanged() {
-        if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
-        mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
-        mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
-    }
-
-    void postCheckIdleStates(int userId) {
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
-    }
-
-    /**
-     * We send a different message to check idle states once, otherwise we would end up
-     * scheduling a series of repeating checkIdleStates each time we fired off one.
-     */
-    void postOneTimeCheckIdleStates() {
-        if (mDeviceIdleController == null) {
-            // Not booted yet; wait for it!
-            mPendingOneTimeCheckIdleStates = true;
-        } else {
-            mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
-            mPendingOneTimeCheckIdleStates = false;
-        }
-    }
-
-    /**
-     * Check all running users' or specified user's apps to see if they enter an idle state.
-     * @return Returns whether checking should continue periodically.
-     */
-    boolean checkIdleStates(int checkUserId) {
-        if (!mAppIdleEnabled) {
-            return false;
-        }
-
-        final int[] runningUserIds;
-        try {
-            runningUserIds = ActivityManager.getService().getRunningUserIds();
-            if (checkUserId != UserHandle.USER_ALL
-                    && !ArrayUtils.contains(runningUserIds, checkUserId)) {
-                return false;
-            }
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-
-        final long elapsedRealtime = SystemClock.elapsedRealtime();
-        for (int i = 0; i < runningUserIds.length; i++) {
-            final int userId = runningUserIds[i];
-            if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
-                continue;
-            }
-            if (DEBUG) {
-                Slog.d(TAG, "Checking idle state for user " + userId);
-            }
-            List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
-                    PackageManager.MATCH_DISABLED_COMPONENTS,
-                    userId);
-            final int packageCount = packages.size();
-            for (int p = 0; p < packageCount; p++) {
-                final PackageInfo pi = packages.get(p);
-                final String packageName = pi.packageName;
-                final boolean isIdle = isAppIdleFiltered(packageName,
-                        UserHandle.getAppId(pi.applicationInfo.uid),
-                        userId, elapsedRealtime);
-                mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
-                        userId, isIdle ? 1 : 0, packageName));
-                if (isIdle) {
-                    synchronized (mAppIdleLock) {
-                        mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
-                    }
-                }
-            }
-        }
-        if (DEBUG) {
-            Slog.d(TAG, "checkIdleStates took "
-                    + (SystemClock.elapsedRealtime() - elapsedRealtime));
-        }
-        return true;
-    }
-
-    /** Check if it's been a while since last parole and let idle apps do some work */
-    void checkParoleTimeout() {
-        boolean setParoled = false;
-        synchronized (mAppIdleLock) {
-            final long now = System.currentTimeMillis();
-            if (!mAppIdleTempParoled) {
-                final long timeSinceLastParole = now - mLastAppIdleParoledTime;
-                if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
-                    if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
-                    setParoled = true;
-                } else {
-                    if (DEBUG) Slog.d(TAG, "Not long enough to go to parole");
-                    postNextParoleTimeout(now);
-                }
-            }
-        }
-        if (setParoled) {
-            setAppIdleParoled(true);
-        }
-    }
-
-    private void notifyBatteryStats(String packageName, int userId, boolean idle) {
-        try {
-            final int uid = mPackageManager.getPackageUidAsUser(packageName,
-                    PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
-            if (idle) {
-                mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
-                        packageName, uid);
-            } else {
-                mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
-                        packageName, uid);
-            }
-        } catch (NameNotFoundException | RemoteException e) {
-        }
-    }
-
-    void onDeviceIdleModeChanged() {
-        final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
-        if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
-        boolean paroled = false;
-        synchronized (mAppIdleLock) {
-            final long timeSinceLastParole = System.currentTimeMillis() - mLastAppIdleParoledTime;
-            if (!deviceIdle
-                    && timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
-                if (DEBUG) {
-                    Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
-                }
-                paroled = true;
-            } else if (deviceIdle) {
-                if (DEBUG) Slog.i(TAG, "Device idle, back to prison");
-                paroled = false;
-            } else {
-                return;
-            }
-        }
-        setAppIdleParoled(paroled);
-    }
-
     private static void deleteRecursively(File f) {
         File[] files = f.listFiles();
         if (files != null) {
@@ -724,76 +355,7 @@
                     getUserDataAndInitializeIfNeededLocked(userId, timeNow);
             service.reportEvent(event);
 
-            synchronized (mAppIdleLock) {
-                // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
-                // about apps that are on some kind of whitelist anyway.
-                final boolean previouslyIdle = mAppIdleHistory.isIdle(
-                        event.mPackage, userId, elapsedRealtime);
-                // Inform listeners if necessary
-                if ((event.mEventType == Event.MOVE_TO_FOREGROUND
-                        || event.mEventType == Event.MOVE_TO_BACKGROUND
-                        || event.mEventType == Event.SYSTEM_INTERACTION
-                        || event.mEventType == Event.USER_INTERACTION)) {
-                    mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
-                    if (previouslyIdle) {
-                        mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
-                                /* idle = */ 0, event.mPackage));
-                        notifyBatteryStats(event.mPackage, userId, false);
-                    }
-                }
-            }
-        }
-    }
-
-    void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
-        // Get sync adapters for the authority
-        String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
-                authority, userId);
-        for (String packageName: packages) {
-            // Only force the sync adapters to active if the provider is not in the same package and
-            // the sync adapter is a system package.
-            try {
-                PackageInfo pi = mPackageManager.getPackageInfoAsUser(
-                        packageName, PackageManager.MATCH_SYSTEM_ONLY, userId);
-                if (pi == null || pi.applicationInfo == null) {
-                    continue;
-                }
-                if (!packageName.equals(providerPkgName)) {
-                    setAppIdleAsync(packageName, false, userId);
-                }
-            } catch (NameNotFoundException e) {
-                // Shouldn't happen
-            }
-        }
-    }
-
-    /**
-     * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
-     * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
-     * the threshold for idle.
-     *
-     * This method is always called from the handler thread, so not much synchronization is
-     * required.
-     */
-    void forceIdleState(String packageName, int userId, boolean idle) {
-        final int appId = getAppId(packageName);
-        if (appId < 0) return;
-        final long elapsedRealtime = SystemClock.elapsedRealtime();
-
-        final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
-                userId, elapsedRealtime);
-        synchronized (mAppIdleLock) {
-            mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
-        }
-        final boolean stillIdle = isAppIdleFiltered(packageName, appId,
-                userId, elapsedRealtime);
-        // Inform listeners if necessary
-        if (previouslyIdle != stillIdle) {
-            mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
-                    /* idle = */ stillIdle ? 1 : 0, packageName));
-            if (!stillIdle) {
-                notifyBatteryStats(packageName, userId, idle);
-            }
+            mAppStandby.reportEvent(event, elapsedRealtime, userId);
         }
     }
 
@@ -813,9 +375,7 @@
         synchronized (mLock) {
             Slog.i(TAG, "Removing user " + userId + " and all data.");
             mUserState.remove(userId);
-            synchronized (mAppIdleLock) {
-                mAppIdleHistory.onUserRemoved(userId);
-            }
+            mAppStandby.onUserRemoved(userId);
             cleanUpRemovedUsersLocked();
         }
     }
@@ -887,253 +447,6 @@
         }
     }
 
-    private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
-        synchronized (mAppIdleLock) {
-            return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
-        }
-    }
-
-    void addListener(AppIdleStateChangeListener listener) {
-        synchronized (mAppIdleLock) {
-            if (!mPackageAccessListeners.contains(listener)) {
-                mPackageAccessListeners.add(listener);
-            }
-        }
-    }
-
-    void removeListener(AppIdleStateChangeListener listener) {
-        synchronized (mAppIdleLock) {
-            mPackageAccessListeners.remove(listener);
-        }
-    }
-
-    int getAppId(String packageName) {
-        try {
-            ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
-                    PackageManager.MATCH_ANY_USER
-                            | PackageManager.MATCH_DISABLED_COMPONENTS);
-            return ai.uid;
-        } catch (NameNotFoundException re) {
-            return -1;
-        }
-    }
-
-    boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime,
-            boolean shouldObfuscateInstantApps) {
-        if (isParoledOrCharging()) {
-            return false;
-        }
-        if (shouldObfuscateInstantApps &&
-                mPackageManagerInternal.isPackageEphemeral(userId, packageName)) {
-            return false;
-        }
-        return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
-    }
-
-    /**
-     * Checks if an app has been idle for a while and filters out apps that are excluded.
-     * It returns false if the current system state allows all apps to be considered active.
-     * This happens if the device is plugged in or temporarily allowed to make exceptions.
-     * Called by interface impls.
-     */
-    private boolean isAppIdleFiltered(String packageName, int appId, int userId,
-            long elapsedRealtime) {
-        if (packageName == null) return false;
-        // If not enabled at all, of course nobody is ever idle.
-        if (!mAppIdleEnabled) {
-            return false;
-        }
-        if (appId < Process.FIRST_APPLICATION_UID) {
-            // System uids never go idle.
-            return false;
-        }
-        if (packageName.equals("android")) {
-            // Nor does the framework (which should be redundant with the above, but for MR1 we will
-            // retain this for safety).
-            return false;
-        }
-        if (mSystemServicesReady) {
-            try {
-                // We allow all whitelisted apps, including those that don't want to be whitelisted
-                // for idle mode, because app idle (aka app standby) is really not as big an issue
-                // for controlling who participates vs. doze mode.
-                if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
-                    return false;
-                }
-            } catch (RemoteException re) {
-                throw re.rethrowFromSystemServer();
-            }
-
-            if (isActiveDeviceAdmin(packageName, userId)) {
-                return false;
-            }
-
-            if (isActiveNetworkScorer(packageName)) {
-                return false;
-            }
-
-            if (mAppWidgetManager != null
-                    && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
-                return false;
-            }
-
-            if (isDeviceProvisioningPackage(packageName)) {
-                return false;
-            }
-        }
-
-        if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {
-            return false;
-        }
-
-        // Check this last, as it is the most expensive check
-        // TODO: Optimize this by fetching the carrier privileged apps ahead of time
-        if (isCarrierApp(packageName)) {
-            return false;
-        }
-
-        return true;
-    }
-
-    int[] getIdleUidsForUser(int userId) {
-        if (!mAppIdleEnabled) {
-            return new int[0];
-        }
-
-        final long elapsedRealtime = SystemClock.elapsedRealtime();
-
-        List<ApplicationInfo> apps;
-        try {
-            ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager()
-                    .getInstalledApplications(/* flags= */ 0, userId);
-            if (slice == null) {
-                return new int[0];
-            }
-            apps = slice.getList();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-
-        // State of each uid.  Key is the uid.  Value lower 16 bits is the number of apps
-        // associated with that uid, upper 16 bits is the number of those apps that is idle.
-        SparseIntArray uidStates = new SparseIntArray();
-
-        // Now resolve all app state.  Iterating over all apps, keeping track of how many
-        // we find for each uid and how many of those are idle.
-        for (int i = apps.size() - 1; i >= 0; i--) {
-            ApplicationInfo ai = apps.get(i);
-
-            // Check whether this app is idle.
-            boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
-                    userId, elapsedRealtime);
-
-            int index = uidStates.indexOfKey(ai.uid);
-            if (index < 0) {
-                uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0));
-            } else {
-                int value = uidStates.valueAt(index);
-                uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
-            }
-        }
-        if (DEBUG) {
-            Slog.d(TAG, "getIdleUids took " + (SystemClock.elapsedRealtime() - elapsedRealtime));
-        }
-        int numIdle = 0;
-        for (int i = uidStates.size() - 1; i >= 0; i--) {
-            int value = uidStates.valueAt(i);
-            if ((value&0x7fff) == (value>>16)) {
-                numIdle++;
-            }
-        }
-
-        int[] res = new int[numIdle];
-        numIdle = 0;
-        for (int i = uidStates.size() - 1; i >= 0; i--) {
-            int value = uidStates.valueAt(i);
-            if ((value&0x7fff) == (value>>16)) {
-                res[numIdle] = uidStates.keyAt(i);
-                numIdle++;
-            }
-        }
-
-        return res;
-    }
-
-    void setAppIdleAsync(String packageName, boolean idle, int userId) {
-        if (packageName == null) return;
-
-        mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
-                .sendToTarget();
-    }
-
-    private boolean isActiveDeviceAdmin(String packageName, int userId) {
-        DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
-        if (dpm == null) return false;
-        return dpm.packageHasActiveAdmins(packageName, userId);
-    }
-
-    /**
-     * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
-     * returns {@code false}.
-     */
-    private boolean isDeviceProvisioningPackage(String packageName) {
-        String deviceProvisioningPackage = getContext().getResources().getString(
-                com.android.internal.R.string.config_deviceProvisioningPackage);
-        return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
-    }
-
-    private boolean isCarrierApp(String packageName) {
-        synchronized (mAppIdleLock) {
-            if (!mHaveCarrierPrivilegedApps) {
-                fetchCarrierPrivilegedAppsLA();
-            }
-            if (mCarrierPrivilegedApps != null) {
-                return mCarrierPrivilegedApps.contains(packageName);
-            }
-            return false;
-        }
-    }
-
-    void clearCarrierPrivilegedApps() {
-        if (DEBUG) {
-            Slog.i(TAG, "Clearing carrier privileged apps list");
-        }
-        synchronized (mAppIdleLock) {
-            mHaveCarrierPrivilegedApps = false;
-            mCarrierPrivilegedApps = null; // Need to be refetched.
-        }
-    }
-
-    @GuardedBy("mAppIdleLock")
-    private void fetchCarrierPrivilegedAppsLA() {
-        TelephonyManager telephonyManager =
-                getContext().getSystemService(TelephonyManager.class);
-        mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
-        mHaveCarrierPrivilegedApps = true;
-        if (DEBUG) {
-            Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
-        }
-    }
-
-    private boolean isActiveNetworkScorer(String packageName) {
-        NetworkScoreManager nsm = (NetworkScoreManager) getContext().getSystemService(
-                Context.NETWORK_SCORE_SERVICE);
-        return packageName != null && packageName.equals(nsm.getActiveScorerPackage());
-    }
-
-    void informListeners(String packageName, int userId, boolean isIdle) {
-        for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
-            listener.onAppIdleStateChanged(packageName, userId, isIdle);
-        }
-    }
-
-    void informParoleStateChanged() {
-        final boolean paroled = isParoledOrCharging();
-        for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
-            listener.onParoleStateChanged(paroled);
-        }
-    }
-
     private static boolean validRange(long currentTime, long beginTime, long endTime) {
         return beginTime <= currentTime && beginTime < endTime;
     }
@@ -1143,15 +456,10 @@
         for (int i = 0; i < userCount; i++) {
             UserUsageStatsService service = mUserState.valueAt(i);
             service.persistActiveStats();
-            synchronized (mAppIdleLock) {
-                mAppIdleHistory.writeAppIdleTimes(mUserState.keyAt(i));
-            }
+            mAppStandby.flushToDisk(mUserState.keyAt(i));
         }
-        // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
-        // considered not-idle, which is the safest outcome in such an event.
-        synchronized (mAppIdleLock) {
-            mAppIdleHistory.writeAppIdleDurations();
-        }
+        mAppStandby.flushDurationsToDisk();
+
         mHandler.removeMessages(MSG_FLUSH_TO_DISK);
     }
 
@@ -1166,7 +474,8 @@
 
             final int userCount = mUserState.size();
             for (int i = 0; i < userCount; i++) {
-                idpw.printPair("user", mUserState.keyAt(i));
+                int userId = mUserState.keyAt(i);
+                idpw.printPair("user", userId);
                 idpw.println();
                 idpw.increaseIndent();
                 if (argSet.contains("--checkin")) {
@@ -1176,57 +485,19 @@
                     idpw.println();
                     if (args.length > 0) {
                         if ("history".equals(args[0])) {
-                            synchronized (mAppIdleLock) {
-                                mAppIdleHistory.dumpHistory(idpw, mUserState.keyAt(i));
-                            }
+                            mAppStandby.dumpHistory(idpw, userId);
                         } else if ("flush".equals(args[0])) {
-                            UsageStatsService.this.flushToDiskLocked();
+                            flushToDiskLocked();
                             pw.println("Flushed stats to disk");
                         }
                     }
                 }
-                synchronized (mAppIdleLock) {
-                    mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
-                }
+                mAppStandby.dumpUser(idpw, userId);
                 idpw.decreaseIndent();
             }
 
             pw.println();
-            synchronized (mAppIdleLock) {
-                pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
-                        + "): " + mCarrierPrivilegedApps);
-            }
-
-            pw.println();
-            pw.println("Settings:");
-
-            pw.print("  mAppIdleDurationMillis=");
-            TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
-            pw.println();
-
-            pw.print("  mAppIdleWallclockThresholdMillis=");
-            TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw);
-            pw.println();
-
-            pw.print("  mCheckIdleIntervalMillis=");
-            TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
-            pw.println();
-
-            pw.print("  mAppIdleParoleIntervalMillis=");
-            TimeUtils.formatDuration(mAppIdleParoleIntervalMillis, pw);
-            pw.println();
-
-            pw.print("  mAppIdleParoleDurationMillis=");
-            TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw);
-            pw.println();
-
-            pw.println();
-            pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
-            pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
-            pw.print(" mCharging="); pw.print(mCharging);
-            pw.print(" mLastAppIdleParoledTime=");
-            TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
-            pw.println();
+            mAppStandby.dumpState(args, pw);
         }
     }
 
@@ -1250,50 +521,6 @@
                     onUserRemoved(msg.arg1);
                     break;
 
-                case MSG_INFORM_LISTENERS:
-                    informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
-                    break;
-
-                case MSG_FORCE_IDLE_STATE:
-                    forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
-                    break;
-
-                case MSG_CHECK_IDLE_STATES:
-                    if (checkIdleStates(msg.arg1)) {
-                        mHandler.sendMessageDelayed(mHandler.obtainMessage(
-                                MSG_CHECK_IDLE_STATES, msg.arg1, 0),
-                                mCheckIdleIntervalMillis);
-                    }
-                    break;
-
-                case MSG_ONE_TIME_CHECK_IDLE_STATES:
-                    mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
-                    checkIdleStates(UserHandle.USER_ALL);
-                    break;
-
-                case MSG_CHECK_PAROLE_TIMEOUT:
-                    checkParoleTimeout();
-                    break;
-
-                case MSG_PAROLE_END_TIMEOUT:
-                    if (DEBUG) Slog.d(TAG, "Ending parole");
-                    setAppIdleParoled(false);
-                    break;
-
-                case MSG_REPORT_CONTENT_PROVIDER_USAGE:
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    reportContentProviderUsage((String) args.arg1, // authority name
-                            (String) args.arg2, // package name
-                            (int) args.arg3); // userId
-                    args.recycle();
-                    break;
-
-                case MSG_PAROLE_STATE_CHANGED:
-                    if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled
-                            + ", Charging state:" + mCharging);
-                    informParoleStateChanged();
-                    break;
-
                 default:
                     super.handleMessage(msg);
                     break;
@@ -1301,72 +528,6 @@
         }
     }
 
-    /**
-     * Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}.
-     */
-    private class SettingsObserver extends ContentObserver {
-        /**
-         * This flag has been used to disable app idle on older builds with bug b/26355386.
-         */
-        @Deprecated
-        private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
-
-        private static final String KEY_IDLE_DURATION = "idle_duration2";
-        private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
-        private static final String KEY_PAROLE_INTERVAL = "parole_interval";
-        private static final String KEY_PAROLE_DURATION = "parole_duration";
-
-        private final KeyValueListParser mParser = new KeyValueListParser(',');
-
-        SettingsObserver(Handler handler) {
-            super(handler);
-        }
-
-        void registerObserver() {
-            getContext().getContentResolver().registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.APP_IDLE_CONSTANTS), false, this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            updateSettings();
-            postOneTimeCheckIdleStates();
-        }
-
-        void updateSettings() {
-            synchronized (mAppIdleLock) {
-                // Look at global settings for this.
-                // TODO: Maybe apply different thresholds for different users.
-                try {
-                    mParser.setString(Settings.Global.getString(getContext().getContentResolver(),
-                            Settings.Global.APP_IDLE_CONSTANTS));
-                } catch (IllegalArgumentException e) {
-                    Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
-                    // fallthrough, mParser is empty and all defaults will be returned.
-                }
-
-                // Default: 12 hours of screen-on time sans dream-time
-                mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
-                       COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
-
-                mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
-                        COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
-
-                mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
-                        COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
-
-                // Default: 24 hours between paroles
-                mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
-                        COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
-
-                mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
-                        COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
-                mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
-                        mAppIdleScreenThresholdMillis);
-            }
-        }
-    }
-
     private final class BinderService extends IUsageStatsManager.Stub {
 
         private boolean hasPermission(String callingPackage) {
@@ -1462,7 +623,8 @@
                     Binder.getCallingUid(), userId);
             final long token = Binder.clearCallingIdentity();
             try {
-                return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId,
+                return mAppStandby.isAppIdleFilteredOrParoled(
+                        packageName, userId,
                         SystemClock.elapsedRealtime(), obfuscateInstantApps);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -1483,9 +645,9 @@
                     "No permission to change app idle state");
             final long token = Binder.clearCallingIdentity();
             try {
-                final int appId = getAppId(packageName);
+                final int appId = mAppStandby.getAppId(packageName);
                 if (appId < 0) return;
-                UsageStatsService.this.setAppIdleAsync(packageName, idle, userId);
+                mAppStandby.setAppIdleAsync(packageName, idle, userId);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -1509,7 +671,7 @@
             getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.BIND_CARRIER_SERVICES,
                     "onCarrierPrivilegedAppsChanged can only be called by privileged apps.");
-            UsageStatsService.this.clearCarrierPrivilegedApps();
+            mAppStandby.clearCarrierPrivilegedApps();
         }
 
         @Override
@@ -1624,28 +786,23 @@
 
         @Override
         public void reportContentProviderUsage(String name, String packageName, int userId) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = name;
-            args.arg2 = packageName;
-            args.arg3 = userId;
-            mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args)
-                    .sendToTarget();
+            mAppStandby.postReportContentProviderUsage(name, packageName, userId);
         }
 
         @Override
         public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
-            return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId,
-                    SystemClock.elapsedRealtime());
+            return mAppStandby.isAppIdleFiltered(packageName, uidForAppId,
+                    userId, SystemClock.elapsedRealtime());
         }
 
         @Override
         public int[] getIdleUidsForUser(int userId) {
-            return UsageStatsService.this.getIdleUidsForUser(userId);
+            return mAppStandby.getIdleUidsForUser(userId);
         }
 
         @Override
         public boolean isAppIdleParoleOn() {
-            return isParoledOrCharging();
+            return mAppStandby.isParoledOrCharging();
         }
 
         @Override
@@ -1658,20 +815,20 @@
 
         @Override
         public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) {
-            UsageStatsService.this.addListener(listener);
+            mAppStandby.addListener(listener);
             listener.onParoleStateChanged(isAppIdleParoleOn());
         }
 
         @Override
         public void removeAppIdleStateChangeListener(
                 AppIdleStateChangeListener listener) {
-            UsageStatsService.this.removeListener(listener);
+            mAppStandby.removeListener(listener);
         }
 
         @Override
         public byte[] getBackupPayload(int user, String key) {
             // Check to ensure that only user 0's data is b/r for now
-            synchronized (UsageStatsService.this.mLock) {
+            synchronized (mLock) {
                 if (user == UserHandle.USER_SYSTEM) {
                     final UserUsageStatsService userStats =
                             getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
@@ -1684,7 +841,7 @@
 
         @Override
         public void applyRestoredPayload(int user, String key, byte[] payload) {
-            synchronized (UsageStatsService.this.mLock) {
+            synchronized (mLock) {
                 if (user == UserHandle.USER_SYSTEM) {
                     final UserUsageStatsService userStats =
                             getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index 5a57f32..f0d60b6 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -16,6 +16,7 @@
 
 package android.telephony.mbms;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.content.Intent;
 import android.net.Uri;
@@ -26,7 +27,6 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.File;
 import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
@@ -71,6 +71,19 @@
         private String appIntent;
         private int version = CURRENT_VERSION;
 
+
+        /**
+         * Builds a new DownloadRequest.
+         * @param sourceUri the source URI for the DownloadRequest to be built. This URI should
+         *     never be null.
+         */
+        public Builder(@NonNull Uri sourceUri) {
+            if (sourceUri == null) {
+                throw new IllegalArgumentException("Source URI must be non-null.");
+            }
+            source = sourceUri;
+        }
+
         /**
          * Sets the service from which the download request to be built will download from.
          * @param serviceInfo
@@ -92,15 +105,6 @@
         }
 
         /**
-         * Sets the source URI for the download request to be built.
-         * @param source
-         */
-        public Builder setSource(Uri source) {
-            this.source = source;
-            return this;
-        }
-
-        /**
          * Set the subscription ID on which the file(s) should be downloaded.
          * @param subscriptionId
          */
@@ -316,9 +320,11 @@
             throw new RuntimeException("Could not get sha256 hash object");
         }
         if (version >= 1) {
-            // Hash the source URI, destination URI, and the app intent
+            // Hash the source URI and the app intent
             digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8));
-            digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
+            if (serializedResultIntentForApp != null) {
+                digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
+            }
         }
         // Add updates for future versions here
         return Base64.encodeToString(digest.digest(), Base64.URL_SAFE | Base64.NO_WRAP);
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index f47d5ca..e3752ac 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -1105,8 +1105,6 @@
     private static final int BASE = Protocol.BASE_WIFI_SCANNER;
 
     /** @hide */
-    public static final int CMD_SCAN                        = BASE + 0;
-    /** @hide */
     public static final int CMD_START_BACKGROUND_SCAN       = BASE + 2;
     /** @hide */
     public static final int CMD_STOP_BACKGROUND_SCAN        = BASE + 3;
@@ -1115,20 +1113,10 @@
     /** @hide */
     public static final int CMD_SCAN_RESULT                 = BASE + 5;
     /** @hide */
-    public static final int CMD_AP_FOUND                    = BASE + 9;
-    /** @hide */
-    public static final int CMD_AP_LOST                     = BASE + 10;
-    /** @hide */
-    public static final int CMD_WIFI_CHANGE_DETECTED        = BASE + 15;
-    /** @hide */
-    public static final int CMD_WIFI_CHANGES_STABILIZED     = BASE + 16;
-    /** @hide */
     public static final int CMD_OP_SUCCEEDED                = BASE + 17;
     /** @hide */
     public static final int CMD_OP_FAILED                   = BASE + 18;
     /** @hide */
-    public static final int CMD_PERIOD_CHANGED              = BASE + 19;
-    /** @hide */
     public static final int CMD_FULL_SCAN_RESULT            = BASE + 20;
     /** @hide */
     public static final int CMD_START_SINGLE_SCAN           = BASE + 21;
@@ -1359,25 +1347,6 @@
                     ScanResult result = (ScanResult) msg.obj;
                     ((ScanListener) listener).onFullResult(result);
                     return;
-                case CMD_PERIOD_CHANGED:
-                    ((ScanListener) listener).onPeriodChanged(msg.arg1);
-                    return;
-                case CMD_AP_FOUND:
-                    ((BssidListener) listener).onFound(
-                            ((ParcelableScanResults) msg.obj).getResults());
-                    return;
-                case CMD_AP_LOST:
-                    ((BssidListener) listener).onLost(
-                            ((ParcelableScanResults) msg.obj).getResults());
-                    return;
-                case CMD_WIFI_CHANGE_DETECTED:
-                    ((WifiChangeListener) listener).onChanging(
-                            ((ParcelableScanResults) msg.obj).getResults());
-                   return;
-                case CMD_WIFI_CHANGES_STABILIZED:
-                    ((WifiChangeListener) listener).onQuiescence(
-                            ((ParcelableScanResults) msg.obj).getResults());
-                    return;
                 case CMD_SINGLE_SCAN_COMPLETED:
                     if (DBG) Log.d(TAG, "removing listener for single scan");
                     removeListener(msg.arg2);
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl
new file mode 100644
index 0000000..0e7289c
--- /dev/null
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.aware;
+
+/**
+ * Callback for IWifiAwareManager.getMacAddressFromPeerHandle
+ *
+ * @hide
+ */
+oneway interface IWifiAwareMacAddressProvider
+{
+    void macAddress(in Map peerIdToMacMap);
+}
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
index f33424b..bad5ce2 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
@@ -21,6 +21,7 @@
 import android.net.wifi.aware.ConfigRequest;
 import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
 import android.net.wifi.aware.IWifiAwareEventCallback;
+import android.net.wifi.aware.IWifiAwareMacAddressProvider;
 import android.net.wifi.aware.PublishConfig;
 import android.net.wifi.aware.SubscribeConfig;
 import android.net.wifi.aware.Characteristics;
@@ -52,4 +53,7 @@
     void sendMessage(int clientId, int discoverySessionId, int peerId, in byte[] message,
         int messageId, int retryCount);
     void terminateSession(int clientId, int discoverySessionId);
+
+    // internal APIs: intended to be used between System Services (restricted permissions)
+    void requestMacAddresses(int uid, in List peerIds, in IWifiAwareMacAddressProvider callback);
 }
diff --git a/wifi/java/android/net/wifi/aware/PeerHandle.java b/wifi/java/android/net/wifi/aware/PeerHandle.java
index cd45c52..1b0aba1 100644
--- a/wifi/java/android/net/wifi/aware/PeerHandle.java
+++ b/wifi/java/android/net/wifi/aware/PeerHandle.java
@@ -32,4 +32,24 @@
 
     /** @hide */
     public int peerId;
+
+    /** @hide RTT_API */
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof PeerHandle)) {
+            return false;
+        }
+
+        return peerId == ((PeerHandle) o).peerId;
+    }
+
+    /** @hide RTT_API */
+    @Override
+    public int hashCode() {
+        return peerId;
+    }
 }